题目链接:https://codeforces.ml/contest/337/problem/D
题目大意:
老题了关键在于转换.
给出一棵树与m个节点,询问有多少个点到这m个点的距离都小于等于p
题目思路:
经典做法:考虑每个点对答案的贡献
对于每个点来言,若与他距离最远的点的距离都小于等于p,那么他对答案的贡献就会+1
考虑树形dp的做法
类似求直径的做法,换根求最长链即可
s代表向下引申的最长链与次长链
t代表向上引申的最长链
这种题之前遇到过没有写博客导致思路还卡了一会儿,这次长记性先写下来
Code:
/*** keep hungry and calm CoolGuang!***/
#include <bits/stdc++.h>
#pragma GCC optimize(3)
//#pragma GCC optimize("Ofast","unroll-loops","omit-frame-pointer","inline")
#include<stdio.h>
#include<queue>
#include<algorithm>
#include<string.h>
#include<iostream>
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const ll INF=2e18;
const int maxn=1e6+6;
const int mod=1e9+7;
const double eps=1e-15;
inline bool read(ll &num)
{char in;bool IsN=false;
in=getchar();if(in==EOF) return false;while(in!='-'&&(in<'0'||in>'9')) in=getchar();if(in=='-'){ IsN=true;num=0;}else num=in-'0';while(in=getchar(),in>='0'&&in<='9'){num*=10,num+=in-'0';}if(IsN) num=-num;return true;}
ll n,m,p;
ll s[maxn][2],t[maxn];
int vis[maxn];
vector<int>v[maxn];
int f[maxn];
void dfs1(int u,int fa){
if(vis[u]) s[u][0] = 0;
f[u] = fa;
for(int e:v[u]){
if(e == fa) continue;
dfs1(e,u);
if(s[u][0]<s[e][0]+1){
s[u][1] = s[u][0];
s[u][0] = s[e][0]+1;
}
else if(s[u][1]<s[e][0]+1) s[u][1] = s[e][0]+1;
}
}
void dfs2(int u,int fa){
ll temp = -INF;
if(vis[u]) t[u] = 0;
if(u != fa){
if(s[u][0]+1 == s[fa][0]) temp = s[fa][1];
else temp = s[fa][0];
t[u] = max(t[fa]+1,temp+1);
}
for(int e:v[u]){
if(e == fa) continue;
dfs2(e,u);
}
}
int main()
{
read(n);read(m);read(p);
for(int i=1;i<=m;i++){
ll x;read(x);
vis[x] = 1;
}
for(int i=1;i<=n-1;i++){
int x,y;scanf("%d%d",&x,&y);
v[x].push_back(y);
v[y].push_back(x);
}
for(int i=1;i<=n;i++) s[i][0] = s[i][1] = t[i] = -INF;
dfs1(1,1);
dfs2(1,1);
ll ans = 0;
for(int i=1;i<=n;i++){
ans += max(s[i][0],t[i])>p?0:1;
}
printf("%lld\n",ans);
return 0;
}
/**
5 2 2
1 5
1 2
2 3
3 4
4 5
**/