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

不难发现只有可分解成 p8p12p22 形式的数才是合法数

可以先筛出 106 以内的所有素数

代码实现
#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 所在连通分量中
注意:在一个连通分量里不可能同时有 AB (因为可以直接用掉)

代码实现
#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] 表示取的最左的数离 la,最右的数距离 rb ,选择了 x 个数的最大权值。不难发现 dp[a][b][x] 是关于 x 上凸的。合并左右儿子信息就需要求闵可夫斯基和,直接归并就好了。
分析一下复杂度,应该是 O(k2nlogn)