[P3942] 将军令
题目大意:
给你一棵树,选择一些点,这些点可以覆盖距离其不超过 \(k\) 的点,问最少选多少点能覆盖所有点。
解题思路:
树形dp,设状态 \(f_{i}\) 表示以i为根的子树中距离i最远的未被防守的点到i的距离, \(g_{i}\) 表示以i为根的子树中距离i最近的防守点到i的距离。
注意到对于每个点若其子树内还有点没被覆盖,则这些点有可能被当前点覆盖,也有可能被更上面的点覆盖(废话),那么我们就可以通过上面的两个dp状态进行判断。
如果 \(f_{x}+g_{x} \le k\) 那么当前子树的所有点就都可被覆盖。
如果 \(f_{x} == k\) 那么当前点必须选。
代码实现:
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
vector<int> v[N];
int n, k, t, ans;
int f[N], g[N];
//fi 表示以i为根的子树中距离i最远的未被防守的点到i的距离。
//gi 表示以i为根的子树中距离i最近的防守点到i的距离。
void dfs(int x, int fa){
f[x] = 0, g[x] = 0x3f3f3f3f;
for(int y : v[x]){
if(y == fa) continue;
dfs(y, x);
f[x] = max(f[x], f[y] + 1);
g[x] = min(g[x], g[y] + 1);
}
if(f[x] + g[x] <= k) f[x] = -0x3f3f3f3f;
else if(f[x] == k) f[x] = -0x3f3f3f3f, g[x] = 0, ans++;
}
int main(){
cin >> n >> k >> t;
for(int i = 1; i < n; i++){
int x, y;
cin >> x >> y;
v[x].push_back(y);
v[y].push_back(x);
}
dfs(1, 1);
cout << ans + (f[1] == 0) << endl;
return 0;
}

浙公网安备 33010602011771号