Description
给一棵树,每条边有权.求一条简单路径,权值和等于K,且边的数量最小.N <= 200000, K <= 1000000
Solution
统计树上路径问题的一大利器就是点分治,然鹅我并不太熟练
这题直接用一个桶,b[x]记录权值和为x路径的最少边数,分治的时候统计一下经过当前重心的答案,即用b[k-dis[x]]+dep[x]更新答案
每次先查询再插入,最后枚举一边删除,这样可以既保证复杂度也可以保证不算重复
Code
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
const int INF=0x3f3f3f3f;
const int N=400005;
const int E=2000005;
struct edge {int x,y,w,next;} e[N];
int ls[N],edCnt;
int size[N],max[N],bct[E];
int dis[N],dep[N];
int root,sum,ans,n,m;
bool vis[N];
void add_edge (int x,int y,int w) {
e[++edCnt]=(edge) {x,y,w,ls[x]}; ls[x]=edCnt;
e[++edCnt]=(edge) {y,x,w,ls[y]}; ls[y]=edCnt;
}
void get_root(int fa,int x) {
size[x]=1; max[x]=0;
for (int i=ls[x];i;i=e[i].next) {
if (e[i].y==fa||vis[e[i].y]) continue;
get_root(x,e[i].y);
size[x]+=size[e[i].y];
max[x]=std:: max(max[x],size[e[i].y]);
}
max[x]=std:: max(max[x],sum-size[x]);
if (!root||max[x]<max[root]) root=x;
}
void calc(int fa,int x) {
if (dis[x]<=m) ans=std:: min(ans,bct[m-dis[x]]+dep[x]);
for (int i=ls[x];i;i=e[i].next) {
if (e[i].y==fa||vis[e[i].y]) continue;
dep[e[i].y]=dep[x]+1;
dis[e[i].y]=dis[x]+e[i].w;
calc(x,e[i].y);
}
}
void add(int fa,int x,int opt) {
if (dis[x]<=m) {
if (opt) bct[dis[x]]=std:: min(bct[dis[x]],dep[x]);
else bct[dis[x]]=INF;
}
for (int i=ls[x];i;i=e[i].next) {
if (e[i].y==fa||vis[e[i].y]) continue;
add(x,e[i].y,opt);
}
}
void solve(int x) {
vis[x]=1; bct[0]=0;
for (int i=ls[x];i;i=e[i].next) {
if (vis[e[i].y]) continue;
dep[e[i].y]=1; dis[e[i].y]=e[i].w;
calc(x,e[i].y);
add(x,e[i].y,1);
}
for (int i=ls[x];i;i=e[i].next) {
if (vis[e[i].y]) continue;
add(x,e[i].y,0);
}
for (int i=ls[x];i;i=e[i].next) {
if (vis[e[i].y]) continue;
root=0; sum=size[e[i].y];
get_root(x,e[i].y);
solve(root);
}
}
int main(void) {
scanf("%d%d",&n,&m); ans=n;
for(int i=1;i<=m;i++) bct[i]=n;
rep(i,2,n) {
int x,y,w; scanf("%d%d%d",&x,&y,&w);
add_edge(++x,++y,w);
}
sum=n; root=0; get_root(0,1);
solve(root);
if (ans==n) ans=-1;
printf("%d\n", ans);
return 0;
}