题意:
给一棵树,每条边有权。求一条简单路径,权值和等于 k,且边的数量最小。
类似模板题,但也有一点不同,变式吧,手打几遍,熟练一下
这道题也可以用树上启发式合并(DSU on Tree)
有一篇很好的博文,全英文,还在研究
dsu on tree
const int inf=0x3f3f3f3f;
const int maxn=1e6+7;
int n,m,tot,head[maxn],d[maxn],ans;
int sum,s[maxn],cnt[maxn],rt;
int a[maxn],f[maxn],b[maxn],q[maxn];
bool vis[maxn];
struct edge{ int v,w,next; }e[maxn<<1];
void add(int u,int v,int w){
e[++tot].v=v; e[tot].w=w;
e[tot].next=head[u]; head[u]=tot;
}
void getrt(int u,int k){
s[u]=1; cnt[u]=0;
for(int i=head[u];i;i=e[i].next){
int v=e[i].v;
if(v==k||vis[v]) continue;
getrt(v,u);
s[u]+=s[v];
cnt[u]=max(cnt[u],s[v]);
}
cnt[u]=max(cnt[u],sum-s[u]);
if(cnt[u]<cnt[rt]) rt=u;
}
//找重心的同时记录边数
void getdis(int u,int k,int step){
if(d[u]>m) return; //剪枝
a[++a[0]]=d[u];
//根据现有的距离,与之前的距离比较,更新答案
ans=min(ans,f[m-d[u]]+step);
//数组q[]暂存各距离的边数
q[a[0]]=step;
for(int i=head[u];i;i=e[i].next){
int v=e[i].v;
if(v==k||vis[v]) continue;
d[v]=d[u]+e[i].w;
getdis(v,u,step+1);
}
}
void cc(int u){
int tmp=0;
for(int i=head[u];i;i=e[i].next){
int v=e[i].v;
if(vis[v]) continue;
//初始化距离,并得到该子树中所有可能的距离
a[0]=0; d[v]=e[i].w;
getdis(v,u,1);
//用临时数组b[]暂存各路径,并更新到各距离的最小边数
for(int j=1;j<=a[0];j++){
b[tmp++]=a[j];
f[a[j]]=min(f[a[j]],q[j]);
}
}
//分治的清零不能用memset
for(int i=0;i<tmp;i++) f[b[i]]=inf;
}
void solve(int u){
vis[u]=1; f[0]=0;
cc(u);
for(int i=head[u];i;i=e[i].next){
int v=e[i].v;
if(vis[v]) continue;
cnt[rt=0]=sum=s[v];
getrt(v,0);
solve(rt);
}
}
int main(){
n=read(); m=read();
memset(f,inf,sizeof(f)); ans=inf;
for(int i=1;i<n;i++){
int u,v,w; u=read()+1; v=read()+1; w=read();
add(u,v,w); add(v,u,w);
}
cnt[rt]=sum=n; getrt(1,0);
solve(rt);
if(ans==inf) cout<<-1;
else cout<<ans;
}
/*
4 3
0 1 1
1 2 2
1 3 4
2
*/