codeforces 337D Book of Evil (树形dp)
题目链接:http://codeforces.com/problemset/problem/337/D
参考博客:http://www.cnblogs.com/chanme/p/3265913
题目大意:给你一个n个点的无向树。任意两点的距离为中间经过的边数。现在某个点上有本魔法书,这本书对与这个点距离小于等于d的点有影响。给你收集到的m个受影响的点(信息不一定全对)。要你判断有多少个点可能放魔法书。
算法思路:我参考别人的想法,自己开始怎么也想不出好的算法,n也太大。这题用树形dp,两遍dfs来统计出每一个点到所有这个m个受影响点的距离的最大值。只要这个最大值<=d,就可能放魔法书。 所以关键就是算这个最大值,有树形dp第一遍就可以统计以u为根,u到子树中存在的受影响点的最大值。第二遍要利用父亲和兄弟节点来求出u到剩余受影响点(即不在u的子树上的点)的最大值。然后和在一起就是u到所有受影响点的最大值。
代码:
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<vector> using namespace std; const int maxn = 101000; const int INF = 0x3f3f3f3f; int disDown[maxn]; //disDown[u]表示以u为根的子树Pm中的点到u距离的最大值。 int disUp[maxn]; //disUp[u]表示以u为根去掉上面的子树中的点,u到与父亲相连的所有pm的最大值。 int Max[maxn]; //Max[u]表示u为根子树Pm中的点到u距离的最大值。 int SecMax[maxn]; //SecMax[u]表示u为根子树Pm中的点到u距离的第二大值。 int n,m,d; vector<int> G[maxn]; void dfs1(int u,int fa) { for(int i=0,sz=G[u].size(); i<sz; i++) { int v = G[u][i]; if(v == fa) continue; dfs1(v,u); if(disDown[v]+1 > SecMax[u]) { SecMax[u] = disDown[v] + 1; if(SecMax[u] > Max[u]) swap(SecMax[u],Max[u]); } } disDown[u]=max(disDown[u],Max[u]); } void dfs2(int u,int fa) { for(int i=0,sz=G[u].size(); i<sz; i++) { int v = G[u][i]; if(v == fa) continue; if(disDown[v] + 1 == Max[u]) disUp[v] = max( disUp[v] , max(disUp[u],SecMax[u])+1 ); else disUp[v] = max( disUp[v] , max(disUp[u],Max[u])+1 ); dfs2(v,u); } } int main() { //freopen("E:\\acm\\input.txt","r",stdin); cin>>n>>m>>d; memset(disDown,-0x3f,sizeof(disDown)); memset(disUp,-0x3f,sizeof(disUp)); memset(Max,-0x3f,sizeof(Max)); memset(SecMax,-0x3f,sizeof(SecMax)); for(int i=1; i<=m; i++) { int pm; scanf("%d",&pm); disDown[pm] = disUp[pm] = 0; } for(int i=1; i<=n; i++) G[i].clear(); for(int i=1; i<n; i++) { int u,v; scanf("%d %d",&u,&v); G[u].push_back(v); G[v].push_back(u); } dfs1(1,-1); dfs2(1,-1); int ans = 0; for(int i=1; i<=n; i++) if(disDown[i] <= d && disUp[i] <= d) ans ++; printf("%d\n",ans); }