1. 程式人生 > >2018.12.08【BZOJ2599】【IOI2011】Race(點分治)

2018.12.08【BZOJ2599】【IOI2011】Race(點分治)

傳送門


解析:

點分治一般是統計問題,而這道是一個最優化問題,所以處理上不方便使用容斥原理。

思路:

那麼怎麼統計呢?

考慮我們維護一個數組 f f ,表示距離當前分治重心距離為 i i 的點,到分治重心路徑上的最小邊數,然後每次處理一棵子樹,直接把子樹內部所有點的資訊先在 f

f 上面詢問一次,看能否湊成 k k , 整棵子樹處理完了之後再把這顆子樹的資訊加到 f f 數組裡面去,供其他子樹查詢的時候用。

分治其他重心之前記住要清空 f

f 陣列,直接 d f s dfs 一次清空就行了。


程式碼:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register #define gc getchar #define pc putchar #define cs const inline int getint(){ re int num; re char c; while(!isdigit(c=gc()));num=c^48; while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48); return num; } cs int N=200005,INF=0x3f3f3f3f; int last[N],nxt[N<<1],to[N<<1],ecnt; int w[N<<1]; inline void addedge(int u,int v,int val){ nxt[++ecnt]=last[u],last[u]=ecnt,to[ecnt]=v,w[ecnt]=val; nxt[++ecnt]=last[v],last[v]=ecnt,to[ecnt]=u,w[ecnt]=val; } cs int M=1000006; int f[M]; int total,maxn,G; int siz[N]; bool ban[N]; inline void find_G(int u,int fa){ siz[u]=1; int mx=0; for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]]){ if(v==fa||ban[v])continue; find_G(v,u); siz[u]+=siz[v]; mx=max(mx,siz[v]); } mx=max(mx,total-siz[u]); if(maxn>=mx)maxn=mx,G=u; } int ans,k,n; int dist[N],dep[N]; inline void calc(int u,int fa){ if(dist[u]<=k&&dep[u]<ans){ ans=min(ans,f[k-dist[u]]+dep[u]); for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]]){ if(v==fa||ban[v])continue; dep[v]=dep[u]+1; dist[v]=dist[u]+w[e]; calc(v,u); } } } inline void update(int u,int fa,bool flag){ if(dist[u]<=k&&dep[u]<ans){ if(flag)f[dist[u]]=min(f[dist[u]],dep[u]); else f[dist[u]]=INF; for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]]){ if(v==fa||ban[v])continue; update(v,u,flag); } } } inline void calc_G(int u){ ban[u]=true;f[0]=0; for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]]){ if(ban[v])continue; dist[v]=w[e];dep[v]=1; calc(v,0); update(v,0,1); } for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]]){ if(ban[v])continue; update(v,0,0); } for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]]){ if(ban[v])continue; total=maxn=siz[v]; find_G(v,u); calc_G(G); } } signed main(){ n=getint(); k=getint(); for(int re i=1;i<n;++i){ int u=getint(),v=getint(),w=getint(); addedge(u,v,w); } total=maxn=n; memset(f,INF,sizeof f); ans=INF; find_G(0,-1); calc_G(G); printf("%d",ans>n?-1:ans); return 0; }