CodeForces

题意:一颗树上有且仅有一只恶魔,恶魔会污染距离它小于等于d的点,现在已经知道被污染的m个点,问恶魔在的可能结点的数量。

 容易想到,要是一个点到(距离最远的两个点)的距离都小于等于d,那么这个点就有可能是恶魔所在的点(虽然我也是没有证明凭感觉,逃~~)。

那么问题就难在怎么快速找到这m个点中距离最远的两个点?我们会想到这跟 找树的直径非常相似,于是就是用找树的直径的方法:树形dp找出这两个点。

然后距离这两个点的距离都小于等于d的就统计答案即可。

然后要注意m=1的情况。

细节请看代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m,d,num,ans,rec,v[N];
vector<int> G[N];

int d1[N],d2[N],r1[N],r2[N];
void dfs1(int x,int fa) {
    if (v[x]) d1[x]=0,r1[x]=x;
    for (int i=0;i<G[x].size();i++) {
        int y=G[x][i];
        if (y==fa) continue;
        dfs1(y,x);
        if (d1[y]+1>=d1[x]) {
            d2[x]=d1[x]; r2[x]=r1[x];
            d1[x]=d1[y]+1; r1[x]=r1[y];
        } 
        else if (d1[y]+1>d2[x]) {
            d2[x]=d1[y]+1; r2[x]=r1[y];
        }
    }
    if (d1[x]+d2[x]>ans) {
        ans=d1[x]+d2[x];
        rec=x;
    }
}

int dep1[N],dep2[N];
void dfs2(int x,int dd,int fa,int *dep) {
    dep[x]=dd;
    for (int i=0;i<G[x].size();i++) {
        int y=G[x][i];
        if (y==fa) continue;
        dfs2(y,dd+1,x,dep);
    }
}

int main()
{
    cin>>n>>m>>d;
    for (int i=1;i<=m;i++) {
        int x; scanf("%d",&x);
        v[x]=1; num=x;
    }
    for (int i=1;i<n;i++) {
        int x,y; scanf("%d%d",&x,&y);
        G[x].push_back(y);
        G[y].push_back(x);
    }
    
    memset(d1,-0x3f,sizeof(d1));
    memset(d2,-0x3f,sizeof(d2));
    ans=0; rec=0;
    dfs1(1,0);
    
    int cnt=0;
    if (m>1) {
        dfs2(r1[rec],0,0,dep1);
        dfs2(r2[rec],0,0,dep2);
        for (int i=1;i<=n;i++)
            if (dep1[i]<=d && dep2[i]<=d) cnt++;
    } else {
        dfs2(num,0,0,dep1);
        for (int i=1;i<=n;i++)
            if (dep1[i]<=d) cnt++;
    }
    cout<<cnt<<endl;
    return 0;
}
原文地址:https://www.cnblogs.com/clno1/p/10735903.html