题目链接:http://codeforces.com/problemset/problem/337/D
题意:
给你一棵树,n个节点。
如果一个节点处放着“罪恶之书”,那么它会影响周围距离不超过d的所有节点。
然后告诉你一部分被影响的节点aff[i],共m个。
已知有且仅有一个节点放着“罪恶之书”。
现在问你有多少个节点可能放着“罪恶之书”。
题解:
如果一个节点放着“罪恶之书”,那么它到所有aff[i]的距离都不超过d。
也就是:max(它到aff[i]的距离) <= d
有一个关于树的直径的结论:
从一个点出发,不重复经过节点,若要使走的路程最远,则最终到达的点一定是树的直径的某个端点。
在这道题中就是:
从一个点出发,若到达aff[i]的距离在所有受影响的节点中最大。
则节点i一定是受影响的点中,两两距离最远的一对点(op,ed)中的一个。
所以要找出在aff[i]中,两两距离最远的一对点(op,ed)。
也就是求所有aff[i]构成的一棵树的直径:
先随便找一个aff[i],从它开始dfs1一遍,找出最远的点即为op。
再从op开始,dfs1一遍,找出ed。
然后从op和ed分别做一次dfs2,给每个距离不超过d的点的cnt加1。
最后统计一下cnt为2的点的个数,即为答案。
AC Code:
1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 #include <vector> 5 #define MAX_N 100005 6 7 using namespace std; 8 9 int n,m,d; 10 int maxd; 11 int op,ed; 12 int aff[MAX_N]; 13 int cnt[MAX_N]; 14 bool flag[MAX_N]; 15 vector<int> edge[MAX_N]; 16 17 void read() 18 { 19 cin>>n>>m>>d; 20 memset(flag,false,sizeof(flag)); 21 for(int i=1;i<=m;i++) 22 { 23 cin>>aff[i]; 24 flag[aff[i]]=true; 25 } 26 int x,y; 27 for(int i=1;i<n;i++) 28 { 29 cin>>x>>y; 30 edge[x].push_back(y); 31 edge[y].push_back(x); 32 } 33 } 34 35 void dfs1(int now,int p,int nd,int &v) 36 { 37 if(nd>maxd && flag[now]) 38 { 39 maxd=nd; 40 v=now; 41 } 42 for(int i=0;i<edge[now].size();i++) 43 { 44 int temp=edge[now][i]; 45 if(temp!=p) 46 { 47 dfs1(temp,now,nd+1,v); 48 } 49 } 50 } 51 52 void dfs2(int now,int p,int stp) 53 { 54 if(stp>d) return; 55 cnt[now]++; 56 for(int i=0;i<edge[now].size();i++) 57 { 58 int temp=edge[now][i]; 59 if(temp!=p) dfs2(temp,now,stp+1); 60 } 61 } 62 63 void work() 64 { 65 maxd=-1; 66 dfs1(aff[1],-1,0,op); 67 maxd=-1; 68 dfs1(op,-1,0,ed); 69 memset(cnt,0,sizeof(cnt)); 70 dfs2(op,-1,0); 71 dfs2(ed,-1,0); 72 int ans=0; 73 for(int i=1;i<=n;i++) 74 { 75 if(cnt[i]==2) ans++; 76 } 77 cout<<ans<<endl; 78 } 79 80 int main() 81 { 82 read(); 83 work(); 84 }