[IOI2011]Race
题目描述
给一棵树,每条边有权。求一条简单路径,权值和等于 K,且边的数量最小。
输入输出格式
输入格式:
第一行:两个整数 n,k。
第二至 n行:每行三个整数,表示一条无向边的两端和权值 (注意点的编号从 0 开始)。
输出格式:
一个整数,表示最小边数量。
如果不存在这样的路径,输出 -1。
输入输出样例
说明
N<=2*10^5,K<=1*10^6。
IOI签到题,裸点分
#include<bits/stdc++.h> #define ll long long #define maxn 200005 #define pb push_back using namespace std; const int inf=1<<29; int tag[maxn*5],n,k; int num,d[maxn],mn,dep[maxn]; bool done[maxn]; int siz[maxn],ans=inf,hd[maxn]; int root,sz,to[maxn*2],tot; int ne[maxn*2],val[maxn*2]; inline void add(int uu,int vv,int ww){ to[++tot]=vv,ne[tot]=hd[uu],hd[uu]=tot,val[tot]=ww; to[++tot]=uu,ne[tot]=hd[vv],hd[vv]=tot,val[tot]=ww; } void find_root(int x,int fa){ siz[x]=1; int bal=0; for(int i=hd[x];i;i=ne[i]){ if(to[i]==fa||done[to[i]]) continue; find_root(to[i],x); siz[x]+=siz[to[i]]; bal=max(bal,siz[to[i]]); } bal=max(bal,sz-siz[x]); if(bal<mn) mn=bal,root=x; } int find_siz(int x,int fa){ int an=1; for(int i=hd[x];i;i=ne[i]) if(to[i]!=fa&&!done[to[i]]){ an+=find_siz(to[i],x); } return an; } void get_dep(int x,int fa,int dis,int dp){ if(dis<=k){ ans=min(ans,dp+tag[k-dis]); d[++num]=dis,dep[num]=dp; } for(int i=hd[x];i;i=ne[i]) if(to[i]!=fa&&!done[to[i]]){ get_dep(to[i],x,dis+val[i],dp+1); } } inline void calc(int x,int dis){ int pre=num; get_dep(x,x,dis,1); for(int i=pre+1;i<=num;i++) tag[d[i]]=min(tag[d[i]],dep[i]); } inline void work(int x,int trsiz){ sz=trsiz,mn=inf; find_root(x,x); done[root]=1; tag[0]=0; for(int i=hd[root];i;i=ne[i]) if(!done[to[i]]){ calc(to[i],val[i]); } for(int i=1;i<=num;i++) tag[d[i]]=inf; num=0,tag[0]=inf; for(int i=hd[root];i;i=ne[i]) if(!done[to[i]]){ work(to[i],find_siz(to[i],to[i])); } } int main(){ memset(tag,0x3f,sizeof(tag)); int uu,vv,ww; scanf("%d%d",&n,&k); for(int i=1;i<n;i++){ scanf("%d%d%d",&uu,&vv,&ww); add(uu,vv,ww); } work(1,n); if(ans==inf) puts("-1"); else printf("%d\n",ans); return 0; }
我爱学习,学习使我快乐