題目連結: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 }