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
",ans);
}
View Code
原文地址:https://www.cnblogs.com/acmdeweilai/p/3330800.html