AtCoder Beginner Contest 364 補題記錄(A~F)

yhbqwq發表於2024-07-28

VP 五十八分鐘蘇童流體。好耶

A

#define GLIBCXX_DEBUG
#include <iostream>
#include <cstring>
#include <cstdio>
#define int long long
const int N = 500100;
std::string s[N];
signed main() {
    int n, cnt = 1;
    scanf("%lld", &n);
    for (int i = 1; i <= n; ++i)
        std::cin >> s[i];
    for (int i = 2; i < n; ++i) {
        if (s[i] == "sweet" && s[i - 1] == "sweet") {
            printf("No\n");
            return 0;
        }
    }
    puts("Yes");
    return 0;
}

B

直接模擬即可。

#define GLIBCXX_DEBUG
#include <iostream>
#include <cstring>
#include <cstdio>
#define int long long
const int N = 500100;
char s[510][510];
signed main() {
    int n, m;
    scanf("%lld%lld", &n, &m);
    int x, y;
    scanf("%lld%lld", &x, &y);
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= m; ++j)
            std::cin >> s[i][j];
    std::string xx;
    std::cin >> xx;
    for (auto &v : xx) {
        if (v == 'U') {
            if (x > 1 && s[x - 1][y] == '.')
                --x;
        } else if (v == 'D') {
            if (x < n && s[x + 1][y] == '.')
                ++x;
        } else if (v == 'L') {
            if (y > 1 && s[x][y - 1] == '.')
                --y;
        } else {
            if (y < m && s[x][y + 1] == '.')
                ++y;
        }
    }
    std::cout << x << ' ' << y << '\n';
    return 0;
}

C

簡單貪心。容易發現甜度和鹹度互相獨立,因此分類甜度最少多少次,鹹度最少多少次即可。排一下序就可以求出答案。

時間複雜度為 \(O(n\log n)\)

#define GLIBCXX_DEBUG
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#define int long long
const int N = 500100;
int a[N], b[N];
signed main() {
    int n, x, y;
    std::cin >> n >> x >> y;
    for (int i = 1; i <= n; ++i)
        std::cin >> a[i];
    for (int i = 1; i <= n; ++i)
        std::cin >> b[i];
    std::sort(a + 1, a + n + 1, std::greater<>());
    std::sort(b + 1, b + n + 1, std::greater<>());
    int c1 = 0, c2 = 0, s1 = 0, s2 = 0;
    for (int i = 1; i <= n; ++i) {
        s1 += a[i];
        ++c1;
        if (s1 > x) break;
    }
    for (int i = 1; i <= n; ++i) {
        s2 += b[i];
        ++c2;
        if (s2 > y) break;
    }
    std::cout << std::min(c1, c2) << '\n';
    return 0;
}

D

二分套二分板子。考慮對於每一個詢問二分距離,然後兩個二分分別二分出左邊和右邊最多可以選擇多少個點,判斷選擇的點數是否超過了 \(k\) 即可。稍微有一點點細節。時間複雜度為 \(O(n\log^2n)\)。不知道有沒有單 \(\log\) 做法。

#define GLIBCXX_DEBUG
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#define int long long
const int N = 500100;
int a[N], b[N];
signed main() {
    int n, x, y;
    std::cin >> n >> x >> y;
    for (int i = 1; i <= n; ++i)
        std::cin >> a[i];
    for (int i = 1; i <= n; ++i)
        std::cin >> b[i];
    std::sort(a + 1, a + n + 1, std::greater<>());
    std::sort(b + 1, b + n + 1, std::greater<>());
    int c1 = 0, c2 = 0, s1 = 0, s2 = 0;
    for (int i = 1; i <= n; ++i) {
        s1 += a[i];
        ++c1;
        if (s1 > x) break;
    }
    for (int i = 1; i <= n; ++i) {
        s2 += b[i];
        ++c2;
        if (s2 > y) break;
    }
    std::cout << std::min(c1, c2) << '\n';
    return 0;
}

E

這不 AT_dp_e。最簡單的思路是 \(f_{i,j,k}\) 表示當前選擇前 \(i\) 個菜餚,甜度為 \(j\),鹹度為 \(k\) 最多可以選擇多少個菜餚。但是顯然過不去。考慮交換狀態。設 \(f_{i,j,k}\) 表示當前選擇前 \(i\) 個菜餚,選了 \(j\) 個菜餚,當前甜度為 \(k\) 的最小鹹度為多少。式子顯然。

時間複雜度為 \(O(n^2\min(X,Y))\)

#define GLIBCXX_DEBUG
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
// #define int long long
const int N = 500100;
int a[N], b[N], f[83][83][10010];
signed main() {
    std::ios_base::sync_with_stdio(0);
    std::cin.tie(0);
    int n, x, y;
    std::cin >> n >> x >> y;
    for (int i = 1; i <= n; ++i)
        std::cin >> a[i] >> b[i];
    memset(f, 0x3f, sizeof f);
    f[0][0][0] = 0;
    // f[i][j][k] 表示當前吃了前 i 個菜餚,選擇 j 個菜餚,當前甜度為 k 的最小鹹度
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= i; ++j)
            for (int k = a[i]; k <= x; ++k)
                for (int p = 0; p < i; ++p)
                    f[i][j][k] = std::min(f[i][j][k], f[p][j - 1][k - a[i]] + b[i]);
    int res = 0;
    for (int i = 1; i <= n; ++i)
    for (int j = 0; j <= n; ++j)
        for (int k = 0; k <= x; ++k)
            if (f[i][j][k] <= y)
                res = std::max(res, j);
    if (res != n)
        ++res;
    std::cout << res << '\n';
    return 0;
}

F

首先把所有的邊離線下來按照代價從小到大排序。

然後對於每一條邊 \(l\sim r\),若其前面的所有的邊都已經加入最小生成樹中,則可以考慮維護任意相鄰的兩個點 \(i,i+1\) 之間的連通性。每一次操作完畢之後一定貪心的讓 \(l\sim r\) 中所有的點都連通,也就是說 \(l\sim r-1\) 之間的邊全部刪除。

直接暴力列舉顯然不可行。因此考慮用一個 set 來維護答案,每一次用 lower_bound 找到其下一次要刪除的邊並將其剎刪除。很明顯每一條邊最多隻會被刪除 \(1\) 次。因此時間複雜度為 \(O(n\log n)\) 可以透過。

#define GLIBCXX_DEBUG
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <set>
#include <algorithm>
#define int long long
const int N = 2000100;
struct awa {
    int a, b, c;
} wx[N];
bool operator<(const awa &l, const awa &r) {
    return l.c < r.c;
}
int la[N];
signed main() {
    int n, q;
    scanf("%lld%lld", &n, &q);
    for (int i = 1; i <= q; ++i)
        scanf("%lld%lld%lld", &wx[i].a, &wx[i].b, &wx[i].c);
    std::sort(wx + 1, wx + q + 1);
    std::set<int> se;
    for (int i = 1; i <= n; ++i)
        se.insert(i), la[i] = i;
    int cost = 0;
    for (int i = 1; i <= q; ++i) {
        auto it = se.lower_bound(wx[i].a);
        int pos = la[*it], cnt = 0, tm;
        for (; it != se.end() && wx[i].b >= la[*it]; se.erase(tm))
            tm = *it, ++it, ++cnt;
        se.insert(tm), la[tm] = pos;
        cost += cnt * wx[i].c;
    }
    if (se.size() == 1)
        printf("%lld\n", cost);
    else
        puts("-1");
}

相關文章