【刷题笔记】p1084疫情控制
思路
首先注意到答案具有单调性,可以二分答案。
考虑 check 函数怎么写。
可以发现一个显然的性质:一个军队的深度越小,他所能控制的点越多。
因此,当总时间确定时,我们可以让每个军队在规定的时间内尽量往上跳,发现这个东西可以用树上倍增维护。
但是,题目有一个坑点:根节点不能放军队,这就使得树上的军队(当前军队为 u)分为两类(dep 数组表示到根节点的距离,x 表示二分的时间):
- x<=dep[u],即不能跨越根节点
- x>dep[u],即可以跨越根节点
对于第一类,显然很好处理,让他尽量往上跳就可以;
但是对于第二类,就有点麻烦,有一个非常 naive 的做法就是把所有可以跨越根节点的军队的剩余距离(跳到根节点后还可以调多少)按从大到小排序,把根节点到所有子树未安放军队的儿子的距离按从大到小排序,然后一一匹配。
但是这样是错的,为什么呢,假设一个军队他已经跳到了根节点的儿子处,他的剩余距离很大,但是小于当前儿子到根节点的距离,并且他跳走后会留下一个空缺,这时他跨越根节点后流下的空缺需要一个剩余距离比他还大的军队去补他的空缺,这样肯定不如他留在当前儿子。
因此我们得到了一个性质:若跳到根节点的儿子后,当前军队的剩余距离小于儿子到根的距离,并且他跳走后会留下空缺,那么他就不跳
把不需要跳的节点处理完后,再按从大到小贪心匹配就可以了。
code
#include<bits/stdc++.h>
#define N 100010
#define int long long
using namespace std;
int n, m, cnt = 0, head[N], p[N];
int l = 0, r = 0;
int dep[N], f[N][30], d[N][30], need[N];
int son[N], num1[N], num2[N];
struct edeg{
int to, next, w;
}e[N << 1];
struct node{
int id, w;
}num[N];
void add(int x, int y, int w){
e[++cnt].to = y;
e[cnt].next = head[x];
e[cnt].w = w;
head[x] = cnt;
}
void dfs(int u, int fa, int w){
dep[u] = dep[fa] + w;
f[u][0] = fa, d[u][0] = w;
for(int i = 1; (1 << i) <= 20; i++){
f[u][i] = f[f[u][i - 1]][i - 1];
d[u][i] = d[u][i - 1] + d[f[u][i - 1]][i - 1];
}
for(int i = head[u]; i; i = e[i].next){
int v = e[i].to, w = e[i].w;
if(v == fa) continue;
dfs(v, u, w);
}
}
int dfs1(int u, int fa){
int s = -1;
for(int i = head[u]; i; i = e[i].next){
int v = e[i].to;
if(v == fa) continue;
if(s != -1) s &= dfs1(v, u);
else s = dfs1(v, u);
}
if(s != -1) need[u] |= s;
return need[u];
}
bool cmp(node a, node b){
return a.w < b.w;
}
bool cmp1(int a, int b){
return a > b;
}
bool check(int x){
memset(need, 0, sizeof(need));
int tot1 = 0, tot2 = 0, tot = 0;
for(int i = 1; i <= m; i++){
int u = p[i], res = 0;
if(x < dep[u]){
for(int j = 20; j >= 0; j--){
if(d[u][j] + res <= x && f[u][j] != 1 && f[u][j])
res += d[u][j], u = f[u][j];
}
need[u] = 1;
//cout << u << ' ';
}
else{
for(int j = 20; j >= 0; j--){
if(f[u][j] != 1 && f[u][j])
res += d[u][j], u = f[u][j];
}
res = x - res - d[u][0];
num[++tot].id = u, num[tot].w = res;
}
}
dfs1(1, 0);
sort(num + 1, num + tot + 1, cmp);
//for(int i = 1; i <= n; i++) cout << need[i] << ' ';
for(int i = 1; i <= tot; i++){
int u = num[i].id, w = num[i].w;
if(!need[u] && d[u][0] > w){
need[u] = 1;
}
else{
num1[++tot1] = w;
}
}
for(int i = head[1]; i; i = e[i].next){
int v = e[i].to, w = e[i].w;
if(!need[v]) num2[++tot2] = w;
}
if(tot1 < tot2) return 0;
sort(num1 + 1, num1 + tot1 + 1, cmp1);
sort(num2 + 1, num2 + tot2 + 1, cmp1);
for(int i = 1; i <= tot2; i++){
if(num1[i] < num2[i]) return 0;
}
return 1;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin >> n;
for(int i = 1; i < n; i++){
int x, y, w; cin >> x >> y >> w;
add(x, y, w), add(y, x, w);
r += w;
}
dfs(1, 0, 0);
cin >> m;
for(int i = 1; i <= m; i++) cin >> p[i];
while(l < r){
int mid = (l + r) >> 1;
if(check(mid)) r = mid;
else l = mid + 1;
}
if(check(l)) cout << l;
else cout << -1;
//cout << check(105);
return 0;
}

浙公网安备 33010602011771号