51nod 1378:夹克老爷的愤怒
题目链接:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1378
题目大意:给出一颗结点为$n(1 leqslant n leqslant 100000 )$的树,若每设置一个点使得距离该点不超过$k(0leqslant k leqslant n )$的结点均被覆盖,问全部覆盖整颗树需要设置的最少的点为多少.
显然从叶结点开始向根延伸,每到必须放置的时候放置一个点,遍历到根时若根没被覆盖则放置点个数$+1$,这种方法得到的放置点个数最少(考虑若从根开始向叶结点延伸,遍历到叶节点时若叶节点没被覆盖则放置点个数$+M$,$M$为没被覆盖的叶节点数).
具体操作为dfs返回每个点向上未被覆盖的距离$dis(-k leqslant dis leqslant k)$.
对于结点$u$,维护其每个孩子$v$返回$dis$的最大值$maxn$及最小值$minn$,若$maxn leqslant k$,则在结点$u$放置覆盖点.
复杂度为$O(n)$.
1 #include <cstdio> 2 #include <vector> 3 #define N 100005 4 using namespace std; 5 int ans,x,y,n,k; 6 vector<int>e[N]; 7 int dfs(int x,int f){ 8 if(e[x].size()==1&&e[x][0]==f)return 1; 9 int minn=k+1,maxn=-k-1; 10 for(int i=0;i<(int)e[x].size();++i)if(e[x][i]!=f){ 11 int d=dfs(e[x][i],x); 12 maxn=max(maxn,d); 13 minn=min(minn,d); 14 } 15 if(maxn>=k){ 16 ans++; 17 return -k; 18 }else return (minn+maxn<0?minn:maxn)+1; 19 } 20 int main(void){ 21 scanf("%d%d",&n,&k); 22 for(int i=1;i<n;++i){ 23 scanf("%d%d",&x,&y); 24 e[x].push_back(y); 25 e[y].push_back(x); 26 } 27 if(k==0){ 28 printf("%d ",n); 29 }else{ 30 if(dfs(0,-1)>0)ans++; 31 printf("%d ",ans); 32 } 33 }