传送门
题目描述
思路
-
求一下每个结点的所有分支路径中第二长的那条路径的长度,如果长度大于K,那么k次操作后这个结点就会被保留。
-
做两次dfs第一次,沿着这个固定结点往下搜索,求得最长路径长度,并且记录当前结点的最长路径是来自哪个结点的(第二次搜索会用到)。
-
为什么做两次搜索
第一次搜索后除了第一个结点,其它结点的最长路径都没有考虑到其经过父节点的那条路径,所以要再用第一次的根节点搜索一次,对每个结点,考虑父节点路径对它的第二长路径答案的影响,要特判父节点的最长路是不是就是自己的路径对其的贡献,如果是,就考虑父节点次长路径,对当前结点的次长路径的影响。
具体实现看代码
#include<bits/stdc++.h>
using namespace std;
const int N = 4E5 + 10;
int ma[N],h[N],to[N << 1],ne[N << 1],dp1[N],dp2[N];
// ma数组记录当前结点的最长路径来自哪个子结点
//dp1数组记录最长路径长度,dp2记录次长路径长度
int cnt = 0;
void add(int x,int y){
to[++cnt] = y;
ne[cnt] = h[x];
h[x] = cnt;
}
int dfs(int u,int fa){
dp1[u] = dp2[u] = 1;
int d1,d2; d1 = d2 = 0;
for(int i = h[u]; ~i; i = ne[i]){
int v = to[i];
if(v == fa)
continue;
int d = dfs(v,u) + 1;
if(d > d1) d2 = d1,d1 = d,ma[u] = v;
else if(d > d2) d2 = d;
}
dp1[u] = max(d1,1);
dp2[u] = max(d2,1);
return dp1[u];
}
void dfs_f(int u,int fa){
if(~fa){
if(ma[fa] != u){
if(dp1[fa] + 1 > dp1[u]){
dp2[u] = dp1[u];
dp1[u] = dp1[fa] + 1;
}
}else if(dp2[fa] + 1 > dp1[u]){
dp2[u] = dp1[u];
dp1[u] = dp2[fa] + 1;
}else dp2[u] = max(dp2[u],dp2[fa] + 1);
}
for(int i = h[u]; ~i; i = ne[i]){
int v = to[i];
if(v == fa)
continue;
dfs_f(v,u);
}
}
int main(){
int T; cin >> T;
while(T--){
int n,k; cin >> n >> k;
memset(h,-1,sizeof h);
cnt = 0;
for(int i = 1; i < n; ++i){
int x,y; cin >> x >> y;
add(x,y); add(y,x);
}
dfs(1,-1);// 第一次搜索求最短路 和 最长路
dfs_f(1,-1);//第二次搜索对每个结点考虑父节点对该结点答案的影响
int ans = 0;
for(int i = 1; i <= n; ++i){// 如果次长路径长度大于k那么这个结点最后就可以被保留
if(dp2[i] > k) ++ans;
}
cout << ans << '\n';
}
return 0;
}