首先我知道这是一道点分治可以过的题.
开始考虑普通的点分治套路,dis[],deep[],用来记录当前子树中到当前根的边权和和边的条数,用T[x]保存边权和为x时的最小边数
结果发现自己不太清楚怎么处理在同一棵子树的情况
仔细想想只要等这一棵子树已经遍历过了再更新T数组,而不是在遍历的过程中直接更新,就避免了统计同一子树下的点对(写完这句话我意识到我就是个傻13)
#include<cstdio>
#include<algorithm>
#define N 200005
#define M 1000005
#define INF 1e9
using namespace std;
int n,k,top,ans,pre,cnt=1,sum,root,head[N];
struct edge{int to,v,next;}e[N*2];
struct p{int deep,dis;}d[N];
inline int read()
{
int a=0,f=1;static char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){a=a*10+c-'0';c=getchar();}
return a*f;
}
inline void add(int x,int y,int v)
{
e[++cnt].next=head[x];
head[x]=cnt;
e[cnt].to=y,e[cnt].v=v;
}
int mx[N],son[N],dis[N],mark[N],deep[N],t[M];
void getr(int x,int fa)
{
mx[x]=0,son[x]=1;
for(int i=head[x];i;i=e[i].next)
{
if(e[i].to==fa||mark[e[i].to]) continue;
getr(e[i].to,x);
son[x]+=son[e[i].to];
mx[x]=max(mx[x],son[e[i].to]);
}
mx[x]=max(mx[x],sum-son [x]);
if(mx[x]<mx[root]) root=x;
}
void getd(int x,int fa)
{
int p=dis[x];
if(p>k) return;
d[++top].deep=deep[x],d[top].dis=dis[x];
ans=min(ans,deep[x]+t[k-p]);
for(int i=head[x];i;i=e[i].next)
{
if(mark[e[i].to]||e[i].to==fa) continue;
dis[e[i].to]=dis[x]+e[i].v;
deep[e[i].to]=deep[x]+1;
if(dis[e[i].to]<=k)
getd(e[i].to,x);
}
}
void cal(int x)
{
dis[x]=0,deep[x]=0;
top=0,pre=1;
for(int i=head[x];i;i=e[i].next)
{
if(!mark[e[i].to])
{
deep[e[i].to]=1;dis[e[i].to]=e[i].v;
getd(e[i].to,x);
for(int i=pre;i<=top;++i)
{
int p=d[i].dis;
if(p<=k)
t[p]=min(t[p],d[i].deep);
}
pre=top+1;
}
}
for(int i=1;i<=top;++i)
{
int p=d[i].dis;
if(p<=k) t[p]=INF;
}
t[0]=0;
}
void solve(int x)
{
cal(x);mark[x]=1;
for(int i=head[x];i;i=e[i].next)
{
if(mark[e[i].to]) continue;
sum=son[e[i].to];root=0;
getr(e[i].to,0);
solve(root);
}
}
int main()
{
n=read(),k=read();
int i,x,y,v;
for(i=1;i<n;++i)
{
x=read(),y=read(),v=read();
++x,++y;
add(x,y,v),add(y,x,v);
}
mx[0]=N,sum=n;ans=INF;
for(int i=1;i<=k;++i) t[i]=INF;
getr(1,0);
solve(root);
printf("%d\n",ans==INF?-1:ans);
return 0;
}