1 条题解
-
0
自动搬运
来自洛谷,原作者为

Alex_Wei
**搬运于
2025-08-24 22:50:00,当前版本为作者最后更新于2023-09-10 18:54:05,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
P9600 [IOI2023] 封锁时刻
设 表示 到 的距离, 表示 到 的距离。
一个原始的想法:显然地, 只会取到 或 。如果 ,那么它对答案贡献 ;如果 ,那么它对答案贡献 。这样问题转化为了非常经典的 Cardboard Box:枚举被取到的最大的 ,那么所有 的 至少取也会取 。将 从小到大排序,枚举 ,设 为 的 和 的 构成的可重集,求出最大的 使得 的前 小的元素之和加上 不超过 。将所有 和 离散化后 BIT 上二分即可。
然而直接这样做是有问题的:城市 由 可达的前提条件是 到 所有城市均可达,也就是 在 方向上的后继 可达。我们求出的方案不一定能满足限制。来看看什么样的贡献是不符合限制的吧:
-
先把后继不存在的情况考虑掉:不妨设 。设 。如果 ,那么它产生的 的贡献显然合法。如果 ,则要求 。因为 ,所以如果 ,那么令 变成 (增加至少 贡献,增加至多 代价), 变成 (减少 贡献,减少 代价)可得更优的方案。
-
等于 且不等于 。不妨设 。设 ,则要求 。首先,因为 且 ,所以 。这说明只要 不为 就行。而 显然不会为 ,否则令 变成 (增加 贡献,增加 代价), 变成 (减少 贡献,减少 代价)可得更优的方案。这说明在不考虑限制时用 Cardboard Box 的做法求出的解在该情况下的贡献是符合限制的。
-
等于 。不妨设 。设 ,。这里又有两种情况:
-
,设为 ,则 ,,且要求 。此时 恰好不在 的简单路径上。
-
如果 ,那么令 变成 (增加 贡献,增加 代价), 变成 (减少 贡献,减少 代价)可得更优的方案。
-
如果 ,那么令 变成 (增加 贡献,增加 代价), 变成 (减少 贡献,减少 代价)得到代价相等的方案。
这说明在不考虑限制时用 Cardboard Box 的做法求出的解在该情况下的贡献要么符合限制,要么可以通过调整得到符合限制且代价不劣的方案。
-
-
唯一的问题发生在 ,即 落在 简单路径上且不等于 的时候。而且我们很容易举出一个这种情况下的反例:由 组成的五元链,且 。答案显然为 ,但我们会求出 使得总贡献为 。
-
但是,如果至少一个点贡献了 ,那么 和 之间所有点都要至少贡献 。从这一点出发,考虑钦定 和 之间所有点至少贡献 。在此基础上做 Cardboard Box(这是容易的)是否就符合限制了呢?
我们来具体分析一下:要求 且 。
- 对于 :因为 ,所以 。若 ,这个条件就一定满足。
- 对于 :
- 如果 ,那么若 ,这个条件就一定满足。
- 如果 ,那么若 ,可以令 变成 , 变成 ,贡献不变,但注意到 和 在链上都是向 偏的,所以代价会减少 (把式子写出来可得同样的结果)。因此,若 ,这个条件就一定满足。
也就是说,只要 且 ,方案就一定合法。而这个先决条件恰好被 “钦定 和 之间所有点至少贡献 " 这一步操作满足了(特别地,对于 或 的情况,因为此时 或 ,所以合法),而该操作是必要条件,即所有合法方案都必须满足的条件,所以该操作不会丢掉合法方案。
别忘了还有不存在一个城市从 均可达的情况,此时答案为最大的 使得 前 小的元素之和不大于 。合法性已经证明过了,且如果方案有城市从 均可达,因为只会将代价算大,所以不影响答案的合法性。
时间复杂度 。
#include <bits/stdc++.h> using namespace std; using ll = long long; using pii = pair<int, int>; constexpr int N = 4e5 + 5; ll z[N]; struct level { ll a, b; bool operator < (const level &z) const { return b < z.b; } } c[N]; namespace Cardboard_Box { int cnt, p[N], q[N]; ll d[N], nu[N], su[N]; void add(int x, int v1, ll v2) { while(x <= cnt) { nu[x] += v1, su[x] += v2; x += x & -x; } } int solve(int n, ll K, level *c, int m, ll *z) { cnt = 0; for(int i = 1; i <= m; i++) d[++cnt] = z[i]; for(int i = 1; i <= n; i++) { d[++cnt] = c[i].a; d[++cnt] = c[i].b - c[i].a; } sort(c + 1, c + n + 1); sort(d + 1, d + cnt + 1); cnt = unique(d + 1, d + cnt + 1) - d - 1; ll sum = 0; memset(nu, 0, cnt + 2 << 3); memset(su, 0, cnt + 2 << 3); for(int i = 1; i <= m; i++) { int pos = lower_bound(d + 1, d + cnt + 1, z[i]) - d; add(pos, 1, z[i]); } for(int i = 1; i <= n; i++) { p[i] = lower_bound(d + 1, d + cnt + 1, c[i].a) - d; q[i] = lower_bound(d + 1, d + cnt + 1, c[i].b - c[i].a) - d; add(p[i], 1, c[i].a); } ll ans = 0; for(int i = 0; i <= n; i++) { if(i) { add(p[i], -1, -c[i].a); add(q[i], 1, c[i].b - c[i].a); K -= c[i].a; } if(K < 0) break; ll p = 0, num = 0, sum = 0; for(int j = __lg(cnt); ~j; j--) { int np = p + (1 << j); if(np > cnt || sum + su[np] > K) continue; p = np, num += nu[p], sum += su[p]; } if(p < cnt) num += (K - sum) / d[p + 1]; ans = max(ans, num + i); } return ans; } } ll dx[N], dy[N]; vector<pii> e[N]; void dfs(int id, int ff, ll *d) { if(ff == -1) d[id] = 0; for(pii _ : e[id]) { int it = _.first; if(it == ff) continue; d[it] = d[id] + _.second; dfs(it, id, d); } } int max_score(int N, int X, int Y, ll K, vector<int> U, vector<int> V, vector<int> W) { for(int i = 0; i < N; i++) e[i].clear(); for(int i = 0; i + 1 < N; i++) { e[U[i]].push_back({V[i], W[i]}); e[V[i]].push_back({U[i], W[i]}); } dfs(X, -1, dx), dfs(Y, -1, dy); vector<ll> arr; for(int i = 0; i < N; i++) { arr.push_back(dx[i]); arr.push_back(dy[i]); } sort(arr.begin(), arr.end()); int ans = 0; ll sum = 0; for(int i = 0; i < arr.size(); i++) { if((sum += arr[i]) > K) break; ans++; } int n = 0, m = 0, cnt = 0; for(int i = 0; i < N; i++) { ll mn = min(dx[i], dy[i]); ll mx = max(dx[i], dy[i]); if(dx[i] + dy[i] == dx[Y]) { cnt++, K -= mn; z[++m] = mx - mn; } else c[++n] = {mn, mx}; } if(K >= 0) ans = max(ans, cnt + Cardboard_Box::solve(n, K, c, m, z)); return ans; } -
- 1
信息
- ID
- 9178
- 时间
- 1000ms
- 内存
- 2048MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者