还是比较简单的树分治了吧(居然是ioi的题目,好开森qwq)。
众所周知,在树分治的过程中是会有重复计算的,因此我们每一次处理当前子树的时候都会减去子树的影响,所以定义ans[i]数组表示经过i条边,距离长度为k的点对数,这样就可以++或者--来维护这个ans数组了,然后就没什么了
吐槽:难道同一个点到一个点再走回来是合法吗?为毛要while(l<=r)wa了几发
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define maxn 200020
using namespace std;
int n,cnt,m,k,vis[maxn],head[maxn],h[maxn],tot,s[maxn],size,dis[maxn],rt,ans[maxn],f[maxn];
struct edge{int v,next,w;}e[maxn*2];
void adde(int a,int b,int c){e[tot].v=b,e[tot].w=c,e[tot].next=head[a];head[a]=tot++;}
struct data{
int d,h;
data(int a=0,int b=0):d(a),h(b){}
bool operator<(const data& b)const {return d<b.d;}
}q[maxn];
void getrt(int u,int fa){
f[u]=0,s[u]=1;
for(int i=head[u],v;i!=-1;i=e[i].next){
if(vis[v=e[i].v]||v==fa)continue;
getrt(v,u);
s[u]+=s[v];
f[u]=max(s[v],f[u]);
}
f[u]=max(f[u],size-f[u]);
if(f[u]<f[rt])rt=u;
}
//计算两点之间的距离等于k
void dfs(int u,int fa){
if(dis[u]>k)return;
q[++cnt]=data(dis[u],h[u]);
for(int i=head[u],v;i!=-1;i=e[i].next ){
if(vis[v=e[i].v]||v==fa)continue;
dis[v]=dis[u]+e[i].w,h[v]=h[u]+1;
dfs(v,u);
}
}
void calc(int u,int d1,int h1,int pos){
dis[u]=d1,h[u]=h1,cnt=0;
dfs(u,u);
sort(q+1,q+1+cnt);
int l=1,r=cnt;
while(l<=r){
while(l<r&&q[l].d+q[r].d>k)r--;
for(int t=r;q[l].d+q[t].d==k;t--)ans[q[l].h+q[t].h]+=pos;
l++;
}
}
void solve(int u){
vis[u]=1;
calc(u,0,0,1);
for(int i=head[u],v;i!=-1;i=e[i].next ){
if(vis[v=e[i].v])continue;
calc(v,e[i].w,1,-1);
f[rt=0]=size=s[v];
getrt(v,u);
solve(rt);
}
}
int main(){
scanf("%d%d",&n,&k);
memset(head,-1,sizeof(head));
for(int a,b,c,i=1;i<n;i++){
scanf("%d%d%d",&a,&b,&c);
a++,b++;
adde(a,b,c),adde(b,a,c);
}
size=n,f[rt=0]=size;
getrt(1,0);
solve(rt);
for(int i=1;i<=n;i++)if(ans[i]>0){printf("%d",i);return 0;}
puts("-1");
return 0;
}