题目链接:https://vjudge.net/problem/UVA-1267

首先我们要把这样一棵无根树转换成有根树,那么树根我们可以直接使用$VOD$。

还有一个性质:如果深度为$d$的一个节点并不能被覆盖,那么我们在它的第$k$级的祖先(父亲为第一级)那里建一个$VOD$是最优的,其实很好证明它不仅能覆盖这些点,还能覆盖更多的点。

思路:

1.进行第一遍dfs,将无根树变成有根树。在建树的过程中用$node[d][i](d>k)$表示第$d$层有不能被覆盖到的点,那么可以避开“按深度排序”这个过程。然后对于$d\leq k$的情况我们直接当它不存在。

2.从$n-1$层倒着遍历,如果有点不能被覆盖到,那么就找它的$k$级祖先,在那里设上$VOD$,然后进行更新。

注意:

1.只需处理叶节点。    2.$u$和$father$之间是无向边!

AC代码:

 #include<cstdio>
#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
using namespace std; const int maxn=;
vector<int> g[maxn],node[maxn];
int n,s,k,fa[maxn];
bool vis[maxn]; void dfs(int u,int f,int d){
fa[u]=f;
int nc=g[u].size();
if(nc==&&d>k) node[d].push_back(u);
for(int i=;i<nc;i++){
int v=g[u][i];
if(v!=f) dfs(v,u,d+);
}
} void dfs2(int u,int f,int d){
vis[u]=;
int nc=g[u].size();
for(int i=;i<nc;i++){
int v=g[u][i];
if(v!=f&&d<k) dfs2(v,u,d+);
}
} int solve(){
int ans=;
memset(vis,,sizeof(vis));
for(int d=n-;d>k;d--){
for(int i=;i<node[d].size();i++){
int u=node[d][i];
if(vis[u]) continue;
int v=u;
for(int j=;j<k;j++) v=fa[v];
dfs2(v,-,);
ans++;
}
}
return ans;
} int main(){
int T;
scanf("%d",&T);
while(T--){
scanf("%d%d%d",&n,&s,&k);
for(int i=;i<=n;i++){
g[i].clear();
node[i].clear();
}
for(int i=;i<n-;i++){
int a,b;
scanf("%d%d",&a,&b);
g[a].push_back(b);
g[b].push_back(a);
}
dfs(s,-,);
printf("%d\n",solve());
}
return ;
}

AC代码

05-11 15:07