CF337D-BookOfEvil-树的直径变式

题目链接

https://www.luogu.org/problemnew/show/CF337D

分析

这题觉得还是挺妙的

一个$naive$的做法,对于每个点为根做一遍搜索,对于距离小于等于$d$的点,计数器$+1$,最后再搜一遍统计计数器等于$m$的点就好了,但是这样做肯定超时

这时候我们就想办法减少搜索次数呗,如果某个点到相距最远的两个被亵渎的点距离都小于等于$d$,那么显然这点是合法的,于是我们就找这两点,然后就像找树的直径一样先随便找一点搜一遍找最远点再从那点为根搜一遍最远点,同时标记深度,最后再从新找到的最远点搜一遍,标记深度同时统计答案

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cctype>
#include <queue>
#include <vector>
#define ll long long
#define ri register int
using std::min;
using std::max;
using std::queue;
template <class T>inline void read(T &x){
x=0;int ne=0;char c;
while(!isdigit(c=getchar()))ne=c=='-';
x=c-48;
while(isdigit(c=getchar()))x=(x<<3)+(x<<1)+c-48;
x=ne?-x:x;return ;
}
const int maxn=100005;
const int inf=0x7fffffff;
struct Edge{
int ne,to;
}edge[maxn<<1];
int h[maxn],num_edge=0;
inline void add_edge(int f,int to){
edge[++num_edge].ne=h[f];
edge[num_edge].to=to;
h[f]=num_edge;
}
int mon[maxn],n,m,d;
bool ok[maxn];
int tmp=-inf,r1,r2,dep[maxn],dep_1[maxn],dep_2[maxn];
void dfs_1(int now,int fa){
if(ok[now]&&dep[now]>tmp){r1=now,tmp=dep[now];}
int v;
for(ri i=h[now];i;i=edge[i].ne){
v=edge[i].to;
if(v==fa)continue;
dep[v]=dep[now]+1;
dfs_1(v,now);
}return ;
}
void dfs_2(int now,int fa){
if(ok[now]&&dep_1[now]>tmp){r2=now,tmp=dep_1[now];}
int v;
for(ri i=h[now];i;i=edge[i].ne){
v=edge[i].to;
if(v==fa)continue;
dep_1[v]=dep_1[now]+1;
dfs_2(v,now);
}return ;
}
int ans=0;
void dfs_3(int now,int fa){
//printf("%d %d %d\n",now,dep_1[now],dep_2[now]);
if(dep_2[now]<=d&&dep_1[now]<=d){ans++;}
int v;
for(ri i=h[now];i;i=edge[i].ne){
v=edge[i].to;
if(v==fa)continue;
dep_2[v]=dep_2[now]+1;
dfs_3(v,now);
}return ;
}
int main(){
int x,y,z;
read(n),read(m),read(d);
memset(ok,0,sizeof(bool)*(n+5));
for(ri i=1;i<=m;i++){
read(mon[i]);
ok[mon[i]]=1;
}
for(ri i=1;i<n;i++){
read(x),read(y);
add_edge(x,y);
add_edge(y,x);
}
tmp=-inf,dep[mon[1]]=0,r1=mon[1];
dfs_1(mon[1],0);
tmp=-inf,dep_1[r1]=0,r2=r1;
dfs_2(r1,0);
tmp=-inf,dep_2[r2]=0,ans=0;
dfs_3(r2,0);
//printf("%d %d\n",r1,r2);
printf("%d\n",ans);
return 0;
}