Description
给一棵树,每条边有权。求一条简单路径,权值和等于 $k$,且边的数量最小。
Solution
点分治,统计答案时只统计经过该节点的所有链(包括以该节点为端点)
每次用桶维护之前的子树中每个路径权值和所对应的最少边数
时间复杂度$O(n \log n)$
#include<iostream> #include<cstring> #include<cstdio> #include<cmath> using namespace std; int n,K,head[200005],tot,allsiz,maxx[200005],root,siz[200005],buc[1000005],cnt,d1[200005],d2[200005],ans=1<<30; bool vst[200005]; struct Edge{ int to,nxt,w; }edge[400005]; inline int read(){ int w=0,f=1; char ch=0; while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();} while(ch>='0'&&ch<='9')w=(w<<1)+(w<<3)+ch-'0',ch=getchar(); return w*f; } void getroot(int k,int fa){ siz[k]=1,maxx[k]=0; for(int i=head[k];i;i=edge[i].nxt){ int v=edge[i].to; if(v!=fa&&!vst[v])getroot(v,k),siz[k]+=siz[v],maxx[k]=max(maxx[k],siz[v]); } maxx[k]=max(maxx[k],allsiz-siz[k]); if(maxx[k]<maxx[root])root=k; } void getdis(int k,int fa,int w1,int w2){ if(w1>K)return; d1[++cnt]=w1,d2[cnt]=w2; for(int i=head[k];i;i=edge[i].nxt){ int v=edge[i].to; if(!vst[v]&&v!=fa)getdis(v,k,edge[i].w+w1,w2+1); } } void calc(int k){ buc[0]=cnt=0; for(int i=head[k];i;i=edge[i].nxt){ int v=edge[i].to; if(!vst[v]){ int tag=cnt; getdis(v,k,edge[i].w,1); for(int j=tag+1;j<=cnt;j++)ans=min(ans,buc[K-d1[j]]+d2[j]); for(int j=tag+1;j<=cnt;j++)buc[d1[j]]=min(buc[d1[j]],d2[j]); } } for(int i=1;i<=cnt;i++)buc[d1[i]]=0x7f7f7f7f; } void solve(int k,int s){ vst[k]=true,calc(k); for(int i=head[k];i;i=edge[i].nxt){ int v=edge[i].to; if(vst[v])continue; root=0,allsiz=(siz[v]>siz[k]?s-siz[k]:siz[v]),getroot(v,0),solve(root,allsiz); } } int main(){ maxx[0]=allsiz=n=read(),K=read(),memset(buc,127,sizeof(buc)); for(int i=1;i<n;i++){ int u=read()+1,v=read()+1,w=read(); edge[++tot]=(Edge){v,head[u],w},head[u]=tot,edge[++tot]=(Edge){u,head[v],w},head[v]=tot; } getroot(1,0),solve(root,allsiz),printf("%d\n",ans>=n?-1:ans); return 0; }[IOI2011]Race