1. 程式人生 > >bzoj2599:[IOI2011]Race (點分治)

bzoj2599:[IOI2011]Race (點分治)

Problem

求權值和等於 KK 的路徑中,邊數的最小值。

Solution

點分治… 用 tmp[i]tmp[i] 表示到重心距離為 ii 的最短邊數 那麼 ans=min(ans,tmp[kdsum[i]]+d[i])ans=min(ans,tmp[k-dsum[i]]+d[i]) 因此每次求答案,再把結果加入 tmptmp

注:清零不能 memsetmemset…手動吧…

Code

#include <cstdio>
#include <cstring>
#include
<algorithm>
using namespace std; #define N 200010 #define M 1000010 #define inf 0x3f3f3f3f int n,k,root=0,num=0,sumroot=0,ans=inf,mx[N],dsum[N],d[N],sz[N],h[N],tmp[M],vis[N]; struct node{int to,z,next;}mp[N<<1]; inline void insert(int x,int y,int z){ mp[++num].to=y;mp[num].z=z;mp[num].next=h[x];h[x]
=num; mp[++num].to=x;mp[num].z=z;mp[num].next=h[y];h[y]=num; } void getroot(int x,int fa){ sz[x]=1;mx[x]=0; for(int i=h[x];i;i=mp[i].next){ int y=mp[i].to;if(y==fa || vis[y]) continue; getroot(y,x);sz[x]+=sz[y];mx[x]=max(mx[x],sz[y]); } mx[x]=max(mx[x],sumroot-sz[x]); if(mx[x]<mx[root]) root=
x; } void calc(int x,int fa){ if(dsum[x]<=k) ans=min(ans,tmp[k-dsum[x]]+d[x]); for(int i=h[x];i;i=mp[i].next){ int y=mp[i].to;if(y==fa || vis[y]) continue; dsum[y]=dsum[x]+mp[i].z;d[y]=d[x]+1; calc(y,x); } } void update(int x,int fa,int t){ if(dsum[x]<=k){ if(t) tmp[dsum[x]]=min(tmp[dsum[x]],d[x]); else tmp[dsum[x]]=inf; } for(int i=h[x];i;i=mp[i].next){ int y=mp[i].to;if(y==fa || vis[y]) continue; update(y,x,t); } } void solve(int x){ vis[x]=1;tmp[0]=0; for(int i=h[x];i;i=mp[i].next){ int y=mp[i].to;if(vis[y]) continue; d[y]=1;dsum[y]=mp[i].z; calc(y,0);update(y,0,1); } for(int i=h[x];i;i=mp[i].next) if(!vis[mp[i].to]) update(mp[i].to,0,0); for(int i=h[x];i;i=mp[i].next){ int y=mp[i].to;if(vis[y]) continue; sumroot=sz[y];root=0;getroot(y,0);solve(root); } } int main(){ scanf("%d%d",&n,&k); for(int i=1;i<=n;i++){ int x,y,z;scanf("%d%d%d",&x,&y,&z); insert(x+1,y+1,z); } for(int i=1;i<=k;i++) tmp[i]=inf; mx[0]=inf;sumroot=n;getroot(1,0); solve(root); if(ans==inf) puts("-1"); else printf("%d\n",ans); return 0; }