[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;
}
posted @ 2024-11-10 20:56  _huangweiliang  阅读(18)  评论(0)    收藏  举报