A. Humidifier 1

模拟

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;

int main() {
    int n;
    cin >> n;
    
    int pre = 0, water = 0;
    rep(i, n) {
        int t, v;
        cin >> t >> v;
        
        water -= t-pre;
        water = max(water, 0);
        water += v;
        pre = t;
    }
    
    cout << water << '\n';
    
    return 0;
}

B. Humidifier 2

模拟

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;

int main() {
    int h, w, d;
    cin >> h >> w >> d;
    
    vector<string> s(h);
    rep(i, h) cin >> s[i];
    
    int ans = 0;
    rep(i1, h)rep(j1, w) {
        if (s[i1][j1] == '#') continue;
        rep(i2, h)rep(j2, w) {
            if (s[i2][j2] == '#') continue;
            if (i1 == i2 and j1 == j2) continue;
            int cnt = 0;
            rep(i, h)rep(j, w) {
                if (s[i][j] == '#') continue;
                bool humid = false;
                if (abs(i-i1)+abs(j-j1) <= d) humid = true;
                if (abs(i-i2)+abs(j-j2) <= d) humid = true;
                if (humid) cnt++;
            }
            ans = max(ans, cnt);
        }
    }
    
    cout << ans << '\n';
    
    return 0;
}

C. Humidifier 3

以加湿器为源点跑多源bfs

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;
using P = pair<int, int>;

const int di[] = {-1, 0, 1, 0};
const int dj[] = {0, 1, 0, -1};

int main() {
    int h, w, d;
    cin >> h >> w >> d;
    
    vector<string> s(h);
    rep(i, h) cin >> s[i];
    
    const int INF = 1001001001;
    vector dist(h, vector<int>(w, INF));
    queue<P> q;
    rep(i, h)rep(j, w) {
        if (s[i][j] == 'H') {
            dist[i][j] = 0;
            q.emplace(i, j);
        }
    }
    while (q.size()) {
        auto [i, j] = q.front(); q.pop();
        rep(v, 4) {
            int ni = i+di[v], nj = j+dj[v];
            if (ni < 0 or nj < 0 or ni >= h or nj >= w) continue;
            if (s[ni][nj] == '#') continue;
            if (dist[ni][nj] != INF) continue;
            dist[ni][nj] = dist[i][j]+1;
            q.emplace(ni, nj);
        }
    }
    
    int ans = 0;
    rep(i, h)rep(j, w) if (dist[i][j] <= d) ans++;
    
    cout << ans << '\n';
    
    return 0;
}

D. 9 Divisors

不难发现只有可分解成 \(p^8\)\(p_1^2p_2^2\) 形式的数才是合法数

可以先筛出 \(10^6\) 以内的所有素数

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;
using ll = long long;

struct Sieve {
	int n;
	vector<int> f, primes;
	Sieve(int n=1): n(n), f(n+1) {
		f[0] = f[1] = -1;
		for (ll i = 2; i <= n; ++i) {
			if (f[i]) continue;
			primes.push_back(i);
			f[i] = i;
			for (ll j = i*i; j <= n; j += i) {
				if (!f[j]) f[j] = i;
			}
		}
	}
	bool isPrime(int x) { return f[x] == x; }
};

int main() {
    ll n;
    cin >> n;
    
    Sieve sieve(1e6);
    auto primes = sieve.primes;
    
    int ans = 0;
    for (int p1 : primes) {
        for (int p2 : primes) {
            if (p2 >= p1) break;
            if ((ll)p1*p1*p2*p2 > n) break;
            ans++;
        }
    }
    for (int p : primes) {
        ll x = 1;
        rep(i, 8) x *= p;
        if (x > n) break;
        ans++;
    }
    
    cout << ans << '\n';
    
    return 0;
}

E. Sum of Max Matching

可以考虑用Kruskal跑MST的过程
假设当前要合并边 \((u, v)\),且这条边对应的边权为 \(w\),不难发现以这条边的边权为最大值的路径数为点 \(u\) 所在连通分量中 \(A\) 点的数量和点 \(v\) 所在连通分量中 \(B\) 点的数量之间取最小值,或者,点 \(A\) 也可以在点 \(v\) 所在的连通分量中,点 \(B\) 也可以在点 \(u\) 所在连通分量中
注意:在一个连通分量里不可能同时有 \(A\)\(B\) (因为可以直接用掉)

代码实现
#include <bits/stdc++.h>
#include <atcoder/all>
using namespace atcoder;
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;
using ll = long long;

int main() {
    int n, m, k;
    cin >> n >> m >> k;
    
    vector<tuple<int, int, int>> es;
    rep(i, m) {
        int a, b, c;
        cin >> a >> b >> c;
        --a; --b;
        es.emplace_back(c, a, b);
    }
    
    ranges::sort(es);
    
    vector<int> A(n), B(n);
    rep(i, k) {
        int a;
        cin >> a;
        --a;
        A[a]++;
    } 
    rep(i, k) {
        int b;
        cin >> b;
        --b;
        B[b]++;
    } 
    
    ll ans = 0;
    dsu uf(n);
    for (auto [c, u, v] : es) {
        if (uf.same(u, v)) continue;
        u = uf.leader(u);
        v = uf.leader(v);
        if (A[v]) swap(u, v);
        if (A[u] and B[v]) {
            int x = min(A[u], B[v]);
            ans += (ll)c*x;
            A[u] -= x; B[v] -= x;
        }
        int sumA = A[u]+A[v];
        int sumB = B[u]+B[v];
        uf.merge(u, v);
        A[uf.leader(u)] = sumA;
        B[uf.leader(u)] = sumB;
    }
    
    cout << ans << '\n';
    
    return 0;
}

F. Diversity

先将所有物品按颜色 \(C\) 排序

dp[i][j][0/1] 表示到第 \(i\) 个物品为止选了若干物品且一定选第 \(i\) 个物品最多花费 \(j\) 元且当前是否增加了颜色种类数时的最大满意度

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;
using ll = long long;

inline void chmax(ll& x, ll y) { if (x < y) x = y; }

int main() {
    int n, x, K;
    cin >> n >> x >> K;
    
    vector<tuple<int, int, int>> items;
    rep(i, n) {
        int p, u, c;
        cin >> p >> u >> c;
        items.emplace_back(c, p, u);
    }
    
    ranges::sort(items);
    
    vector dp(x+1, vector<ll>(2));
    int pre_c = -1;
    for (auto [c, p, u] : items) {
        if (pre_c != c) {
            rep(i, x+1) chmax(dp[i][0], dp[i][1]);
            pre_c = c;
        }
        vector old(x+1, vector<ll>(2));
        swap(dp, old);
        
        rep(i, x+1) {
            rep(j, 2) chmax(dp[i][j], old[i][j]);
            int ni = i+p;
            if (ni <= x) {
                chmax(dp[ni][1], old[i][1]+u);
                chmax(dp[ni][1], old[i][0]+u+K);
            }
        }
    }
    
    ll ans = dp[x][1];
    cout << ans << '\n';
    
    return 0;
}

G. Bar Cover

最好想的方式是直接线段树+凸包闵可夫斯基和。
具体的,每个节点维护 dp[a][b][x] 表示取的最左的数离 \(l\)\(a\),最右的数距离 \(r\)\(b\) ,选择了 \(x\) 个数的最大权值。不难发现 dp[a][b][x] 是关于 \(x\) 上凸的。合并左右儿子信息就需要求闵可夫斯基和,直接归并就好了。
分析一下复杂度,应该是 \(O(k^2n\log n)\)