【ARC116E】Spread of Information 题解 (二分 + 贪心)
AtC 传送门:ARC116E Spread of Information
二分 + 贪心。
题意
一个城市是由 \(n\) 个点 \(n-1\) 条边组成的树,一个点的危险度是指这个点到任意一个消防站的距离的最小值。现在要选择 \(k\) 个点建立消防站,使得所有点的危险度的最大值最小,求这个值。
Solution
看到“最大值最小”,思考二分答案的做法。显然,二分的是所有节点危险度的最大值。
不难想到,check(mid) 函数考虑使用贪心实现。从叶子节点往根节点,如果一个节点到自己子树内的 最远未覆盖子节点 的距离刚好超过了 \(mid\),那么这个节点就必须得放一个消防站。
大致求解的思路如上,具体 check() 函数的写法、最远未覆盖子节点的维护等等详见代码及注释。
Code
#include<bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(register int i = a; i <= b; ++i)
const int maxn = 2e5 + 5;
int n, k, f[maxn], g[maxn];
int cnt, hd[maxn];
int rt, mx, siz[maxn];
struct node{
int to, nxt;
}e[maxn << 1];
inline void add(int u, int v){
e[++cnt] = (node){v, hd[u]}, hd[u] = cnt;
}
bool vis[maxn];
inline int dfs(int u, int fa, int dis){
int res = 0, flg = 0; f[u] = 1e9, g[u] = -1e9;
for(int i = hd[u]; i; i = e[i].nxt){
int v = e[i].to; if(v == fa) continue;
res += dfs(v, u, dis), flg = 1;
f[u] = min(f[u], f[v] + 1),// f 数组维护的是每个节点的危险度
g[u] = max(g[u], g[v] + 1);//g[u] 表示节点 u 到自己最远未覆盖子节点的距离
}
if(f[u] + g[u] <= dis) g[u] = -1e9;
//如果 u 的子树内最近的消防站到 u 子树内最深为覆盖子节点的距离 <= dis 那么 u 及其子树一定可以被覆盖
if(!flg or f[u] == dis + 1) g[u] = max(g[u], 0);
//u 没有被覆盖,等祖先中某一个节点安装消防站将其覆盖
if(g[u] == dis)//需要在 u 建立一个消防站
res += 1, f[u] = 0, g[u] = -1e9;
return res;
}
inline bool chck(int x){
return (dfs(1, 0, x) + (g[1] >= 0)/*还需要在根节点安装消防站*/) <= k;
}
int main(){
scanf("%d%d", &n, &k);
if(k >= n){printf("0\n"); return 0;}
rep(i, 2, n){
int u, v; scanf("%d%d", &u, &v);
add(u, v), add(v, u);
}
int l = 1, r = n;
while(l < r){
int mid = l + r >> 1;
if(chck(mid)) r = mid; else l = mid + 1;
}
printf("%d\n", l);
return 0;
}

浙公网安备 33010602011771号