这次比赛必须要仔细反省,不能再出现暑假的精神状态。
A – 服务器需求
假设我们有很多服务器了,我们现在按天考虑,进行服务器的分配:假设有一个服务器编号集合\(\{id_i\}\),考虑一天天分配若干台,这样就可以保证每一台服务器的分配中不会出现同样的日期。
这样就可以分配完了,直接就有\(\sum a_i \leq |id_i| m\)。然后就是傻逼代码了。
// A.cpp #include <bits/stdc++.h> typedef long long ll; using namespace std; const int MAX_N = 4e5 + 200; ll n, m, q, ai[MAX_N]; int main() { ll ans = 0; multiset<ll> ms; scanf("%lld%lld%lld", &n, &m, &q); for (int i = 1; i <= n; i++) scanf("%lld", &ai[i]), ms.insert(ai[i]), ans += ai[i]; printf("%lld\n", max(*ms.rbegin(), (ll)(double(ans + m - 1) / double(m)))); while (q--) { ll x, y; scanf("%lld%lld", &x, &y); ans -= ai[x], ms.erase(ms.find(ai[x])); ms.insert(ai[x] = y), ans += ai[x]; printf("%lld\n", max(*ms.rbegin(), (ll)(double(ans + m - 1) / double(m)))); } return 0; }
B – 沙漠点列
这道题我在考场上只打了 60 分的特判分,还是很好打的。
考虑正解:先求出多少个连通块,然后再处理出有多少个环,来处理\(limit\)极大的情况;当\(limit\)无法直接包揽全部,那么我们优先把所有的割边进行贡献,然后再来破环贡献割边。用 DFS 的方式找出单元环(如果用 Tarjan 直接找就会 GG),然后排序从大到小选即可。
// B.cpp #include <bits/stdc++.h> using namespace std; const int MAX_N = 1e6 + 200, MAX_E = 2e6 + 2000; int head[MAX_N], current, n, m, limit, ncnt, dfn[MAX_N], low[MAX_N], ptot, stk[MAX_N], tail; int SCC, aff[MAX_N], dist[MAX_N], loops[MAX_N], loop_cnt; struct edge { int to, nxt; } edges[MAX_E << 1]; int read() { char ch = getchar(); int x = 0, f = 1; while (!isdigit(ch)) { if (ch == '-') f = -1; ch = getchar(); } while (isdigit(ch)) x = (x << 3) + (x << 1) + ch - '0', ch = getchar(); return f * x; } void addpath(int src, int dst) { edges[current].to = dst, edges[current].nxt = head[src]; head[src] = current++; } void tarjan(int u, int fa) { dfn[u] = low[u] = ++ptot, stk[++tail] = u; for (int i = head[u]; i != -1; i = edges[i].nxt) if (edges[i].to != fa) if (dfn[edges[i].to] == 0) tarjan(edges[i].to, u), low[u] = min(low[u], low[edges[i].to]); else low[u] = min(low[u], dfn[edges[i].to]); if (dfn[u] == low[u]) { SCC++; do { aff[stk[tail]] = SCC; } while (stk[tail--] != u); } } void find_loop(int u, int fa) { for (int i = head[u]; i != -1; i = edges[i].nxt) if (edges[i].to != fa) if (dist[edges[i].to] == 0) { dist[edges[i].to] = dist[u] + 1; find_loop(edges[i].to, u); } else if (dist[edges[i].to] < dist[u] + 1) loops[++loop_cnt] = dist[u] - dist[edges[i].to] + 1; } int main() { memset(head, -1, sizeof(head)); n = read(), m = read(), limit = read(); for (int i = 1, u, v; i <= m; i++) u = read(), v = read(), addpath(u, v), addpath(v, u); for (int i = 1; i <= n; i++) if (dfn[i] == 0) tarjan(i, 0), ncnt++; // get the non-loop component; ncnt = SCC - ncnt; if (limit <= ncnt) printf("%d\n", SCC + limit - ncnt), exit(0); limit -= ncnt; for (int i = 1; i <= n; i++) if (dist[i] == 0) dist[i] = 1, find_loop(i, 0); int ans = SCC; sort(loops + 1, loops + 1 + loop_cnt); reverse(loops + 1, loops + 1 + loop_cnt); for (int i = 1; i <= loop_cnt; i++) { limit--; ans += min(limit, loops[i] - 1); limit -= loops[i] - 1; if (limit < 0) break; } printf("%d\n", ans); return 0; }
总结
这场比赛严重暴露了思路垃圾的弱点,之后每天都会固定写几道 CF 的贪心保持智商。