【换根dp】9.22小偷
换根都不会了
题目大意
给定一棵$n$个点的树和树上一撮关键点,求到所有$m$个关键点距离的最大值$dis_{max}\le LIM$的点的个数。
$n,m\le 30000,LIM\le 30000$
题目分析
考虑在求出一个点的情况下如何转移到其子节点。
对点$u$最直接关心的状态是$mx[u]$:所有关键点到$u$的最大距离。
对点$u$的子节点$v$来说,$u$能带给它的只是“外面的世界”——$v$子树的补集这块贡献,也就是对于$u$的除了$v$子树的$mx[u]$。
因为$mx[u]$的值只会是"从/不从$v$转移"两个状态,那么相当于需要辅助记一个$dx[u]$:所有关键点到$u$的可重次大距离。
这样做两遍dfs就可以实现换根的dp了。
1 #include<bits/stdc++.h> 2 const int maxn = 30035; 3 const int maxm = 60035; 4 5 int n,m,lim,ans,sum,p[maxn],mx[maxn],dx[maxn]; 6 int edgeTot,head[maxn],nxt[maxm],edges[maxm]; 7 bool tag[maxn]; 8 9 int read() 10 { 11 char ch = getchar(); 12 int num = 0, fl = 1; 13 for (; !isdigit(ch); ch=getchar()) 14 if (ch=='-') fl = -1; 15 for (; isdigit(ch); ch=getchar()) 16 num = (num<<1)+(num<<3)+ch-48; 17 return num*fl; 18 } 19 void addedge(int u, int v) 20 { 21 edges[++edgeTot] = v, nxt[edgeTot] = head[u], head[u] = edgeTot; 22 edges[++edgeTot] = u, nxt[edgeTot] = head[v], head[v] = edgeTot; 23 } 24 void dfs1(int x, int fa) 25 { 26 mx[x] = dx[x] = -1; 27 if (tag[x]) mx[x] = 0; 28 for (int i=head[x]; i!=-1; i=nxt[i]) 29 { 30 int v = edges[i]; 31 if (v==fa) continue; 32 dfs1(v, x); 33 if (mx[v]!=-1&&mx[v]+1 >= mx[x]) dx[x] = mx[x], mx[x] = mx[v]+1; 34 else if (mx[v]!=-1&&mx[v]+1 > dx[x]) dx[x] = mx[v]+1; 35 } 36 } 37 void dfs2(int x, int fa) 38 { 39 for (int i=head[x]; i!=-1; i=nxt[i]) 40 { 41 int v = edges[i], val = 0; 42 if (v==fa) continue; 43 if (mx[x]==mx[v]+1&&mx[v]!=-1) val = dx[x]+1; 44 else val = mx[x]+1; 45 if (val&&val >= mx[v]) dx[v] = mx[v], mx[v] = val; 46 else if (val&&val > dx[v]) dx[v] = val; 47 dfs2(v, x); 48 } 49 if (mx[x] <= lim) ++ans; 50 } 51 int main() 52 { 53 memset(head, -1, sizeof head); 54 n = read(), m = read(), lim = read(); 55 for (int i=1; i<=m; i++) tag[read()] = true; 56 for (int i=1; i<n; i++) addedge(read(), read()); 57 dfs1(1, 0); 58 dfs2(1, 0); 59 printf("%d\n",ans); 60 return 0; 61 }
END