题意:一颗树上有且仅有一只恶魔,恶魔会污染距离它小于等于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