1. 程式人生 > >codeforce 337D Book of Evil ----樹形DP&bfs&樹的直徑

codeforce 337D Book of Evil ----樹形DP&bfs&樹的直徑

其中 div ans 初始 如果 n) mes ont ron

比較經典的老題

題目意思:給你一顆節點數為n的樹,然後其中m個特殊點,再給你一個值d,問你在樹中有多少個點到這m個點的距離都不大於d。

這題的寫法有點像樹的直徑求法,先隨便選擇一個點(姑且設為點1)來遍歷一遍樹,存下所有點到點1的距離。然後在m個特殊點中找到距離點1最遠的點a1.

然後以a1為初始點遍歷一遍樹,求每一個點到a1的距離,存在dp[i]中。並且再在m個點中找到到a1距離最大的點a2.最後再以a2為初始點遍歷一遍樹,求到每一個點到a2的距離dp1[i]。然後for遍歷所有點,如果dp[i]和dp1[i]都不大於d,那麽合法解+1;

這裏可以做一個小證明,如果一個點k還能在m個特殊點中找到距離大於到a1和到a2距離的點s的話,那麽點k到a1的距離加上點k到s的距離將大於k到a1加上k到a2,那麽在第二次搜索中搜到的距離a1最遠的點就不應該是a2而是s,所以不成立。由此反證得知。

具體代碼如下:

#include<iostream>
#include<vector>
#include<queue>
#include<string.h>
using namespace std;
bool pd[100050];
int dp[100050],dp1[100050];
int dd[100050];
vector<int> a[100050];
int bfs(int u)
{
    memset(pd,false,sizeof(pd));
    memset(dp,0,sizeof(dp));
    pd[u]=1
; queue<int> q; q.push(u); while(q.size()!=0) { int t=q.front(); q.pop(); for(int i=0;i<a[t].size();i++) { int v=a[t][i]; if(pd[v]) continue; //cout<<"w="<<w<<endl; pd[v]
=1; dp[v]=dp[t]+1; q.push(v); } } return 1; } int bfs1(int u) { memset(pd,false,sizeof(pd)); memset(dp1,0,sizeof(dp1)); pd[u]=1; queue<int> q; q.push(u); while(q.size()!=0) { int t=q.front(); q.pop(); for(int i=0;i<a[t].size();i++) { int v=a[t][i]; if(pd[v]) continue; //cout<<"w="<<w<<endl; pd[v]=1; dp1[v]=dp1[t]+1; q.push(v); } } return 1; } int main() { int i,j,k,l,x,y,n,m,d; scanf("%d%d%d",&n,&m,&d); // cout<<n<<" "<<m<<" "<<d<<endl; for(i=0;i<m;i++) scanf("%d",&dd[i]); for(i=0;i<n-1;i++) { scanf("%d%d",&x,&y); a[x].push_back(y); a[y].push_back(x); } bfs(1); int lon=0,a1=1; for(i=0;i<m;i++) if(dp[dd[i]]>lon) { lon=dp[dd[i]]; a1=dd[i]; } //cout<<lon<<"??"<<a1<<endl; bfs(a1); lon=0; for(i=0;i<m;i++) if(dp[dd[i]]>lon) { lon=dp[dd[i]]; a1=dd[i]; } //cout<<lon<<"??"<<a1<<endl; int ans=0; bfs1(a1); for(i=1;i<=n;i++) { if(dp[i]<=d&&dp1[i]<=d) ans++; } //for(i=1;i<=n;i++) cout<<dp[i]<<" "<<dp1[i]<<endl; printf("%d\n",ans); }

codeforce 337D Book of Evil ----樹形DP&bfs&樹的直徑