P9600 [IOI2023] 封锁时刻
P9600 [IOI2023] 封锁时刻
设
一个原始的想法:显然地,
然而直接这样做是有问题的:城市
-
先把后继不存在的情况考虑掉:不妨设
i = X 。设j = s(i, Y) 。如果c_i = 0 ,那么它产生的1 的贡献显然合法。如果c_i = dy_i ,则要求c_j \geq dy_j 。因为dy_i > dy_j ,所以如果c_j < dy_j ,那么令c_j 变成dy_j (增加至少1 贡献,增加至多dy_j 代价),c_i 变成0 (减少1 贡献,减少dy_i 代价)可得更优的方案。 -
-
- $j_x = j_y$,设为 $j$,则 $dx_j = dx_i - w(i, j)$,$dy_j = dy_i - w(i, j)$,且要求 $c_j = dy_j$。此时 $i$ 恰好不在 $X, Y$ 的简单路径上。 - 如果 $c_j = 0$,那么令 $c_j$ 变成 $dy_j$(增加 $2$ 贡献,增加 $dy_j$ 代价),$c_i$ 变成 $0$(减少 $2$ 贡献,减少 $dy_i$ 代价)可得更优的方案。 - 如果 $c_j = dx_j$,那么令 $c_j$ 变成 $dy_j$(增加 $1$ 贡献,增加 $dy_j - dx_j$ 代价),$c_i$ 变成 $dx_i$(减少 $1$ 贡献,减少 $dy_i - dx_i$ 代价)得到代价相等的方案。 这说明在不考虑限制时用 Cardboard Box 的做法求出的解在该情况下的贡献要么符合限制,要么可以通过调整得到符合限制且代价不劣的方案。 - 唯一的问题发生在 $j_x\neq j_y$,即 $i$ 落在 $X, Y$ 简单路径上且不等于 $X, Y$ 的时候。而且我们很容易举出一个这种情况下的反例:由 $(1, 2, 2), (2, 3, 1), (3, 4, 1), (4, 5, 2)$ 组成的五元链,且 $K = 3$。答案显然为 $3$,但我们会求出 $c_3 = 3$ 使得总贡献为 $4$。
但是,如果至少一个点贡献了
我们来具体分析一下:要求
- 对于
c_{j_x}\geq dx_{j_x} :因为dx_i \leq dy_i ,所以dx_{j_x} < dy_{j_x} 。若c_{j_x} > 0 ,这个条件就一定满足。 - 对于
c_{j_y}\geq dy_{j_y} :- 如果
dy_{j_y}\leq dx_{j_y} ,那么若c_{j_y} > 0 ,这个条件就一定满足。 - 如果
dy_{j_y} > dx_{j_y} ,那么若c_{j_y} = dx_{j_y} ,可以令c_i 变成dx_i ,c_{j_y} 变成dy_{j_y} ,贡献不变,但注意到i 和j_y 在链上都是向X 偏的,所以代价会减少2w(i, j_y) (把式子写出来可得同样的结果)。因此,若c_{j_y} > 0 ,这个条件就一定满足。
- 如果
也就是说,只要
别忘了还有不存在一个城市从
时间复杂度
#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;
}