1. 程式人生 > >P4149 [IOI2011]Race

P4149 [IOI2011]Race

ont freopen ios math org ioi2011 title ace ref

傳送門

十分顯然的點分治

枚舉所有點作為兩點的LCA

開一個桶$pd$判斷之前子樹內是否出現過此路程

對於每一個子樹都把子樹到根的所有路程dis都考慮匹配

如果 $pd[K-dis]=1$ 那麽就說明存在匹配

然鵝題目還要求在合法匹配中選最少經過邊數的匹配

那麽再開一個數組 $dd$ ,$dd[i]$ 存當路程為 i 時經過的最少邊數

dfs一個子樹時開一個棧,存每個路程($st$)和經過的最少邊數($d$)

那麽dfs完一顆子樹後就枚舉棧中的所有元素,如果$pd[K-st[i]]=1$並且$dd[K-st[i]]+d[st[i]]<ans$,那麽更新ans

再把$st$和$d$分別合並到$pd$和$dd$裏,最後記得還原$d$

這種方法如果不點分治會被卡成$O(n^2)$,所以點分治一波就好了

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
using namespace std;
typedef long long ll;
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while
(ch<0||ch>9) { if(ch==-) f=-1; ch=getchar(); } while(ch>=0&&ch<=9) { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int N=1e6+7,M=1e6+7,INF=1e9+7; int fir[N],from[N<<1],to[N<<1],val[N<<1],cntt; inline void add(int &a,int
&b,int &c) { from[++cntt]=fir[a]; fir[a]=cntt; to[cntt]=b; val[cntt]=c; } int n,K,tot,rt,ans=INF; int sz[N],mx[N]; bool vis[N]; void find_rt(int x,int fa) { sz[x]=1; mx[x]=0; for(int i=fir[x];i;i=from[i]) { int &v=to[i]; if(vis[v]||v==fa) continue; find_rt(v,x); sz[x]+=sz[v]; mx[x]=max(mx[x],sz[v]); } mx[x]=max(mx[x],tot-mx[x]); if(mx[x]<mx[rt]) rt=x; } int st[N],Top,d[M]; bool inst[M];//判斷是否在棧中 void dfs(int x,int fa,int dis,int dep)//dis存當前的路程,dep是深度 { if(dis>K) return;//如果dis>K就不用dfs了,不會對答案產生貢獻 if(!inst[dis]) st[++Top]=dis,d[dis]=dep,inst[dis]=1;//如果dis不在棧就入棧 else if(d[dis]>dep) d[dis]=dep;//否則考慮更新d for(int i=fir[x];i;i=from[i]) { int &v=to[i]; if(vis[v]||v==fa) continue; dfs(v,x,dis+val[i],dep+1); } } int dd[M],q[N],p;//q是回收池 bool pd[M]; void work(int x) { p=0; pd[0]=1; for(int i=fir[x];i;i=from[i]) { int &v=to[i]; if(vis[v]) continue; Top=0; dfs(v,x,val[i],1); for(int j=1;j<=Top;j++)//枚舉所有路程嘗試更新答案 if(pd[K-st[j]]&&dd[K-st[j]]+d[st[j]]<ans) ans=dd[K-st[j]]+d[st[j]]; for(int j=1;j<=Top;j++)//把st和d分別合進pd和dd { if(!pd[st[j]]) pd[st[j]]=1,dd[st[j]]=d[st[j]],q[++p]=st[j]; else if(dd[st[j]]>d[st[j]]) dd[st[j]]=d[st[j]]; d[st[j]]=0,inst[st[j]]=0;//合完記得清空 } } for(int i=1;i<=p;i++) pd[q[i]]=0,dd[q[i]]=0;//最後記得要把pd和dd全部還原 } void solve(int x)//點分治 { vis[x]=1; work(x); for(int i=fir[x];i;i=from[i]) { int &v=to[i]; if(vis[v]) continue; tot=sz[v]; rt=0; find_rt(v,0); solve(rt); } } int main() { //freopen("data.in","r",stdin); //freopen("data.out","w",stdout); n=read(),K=read(); int a,b,c; for(int i=1;i<n;i++) { a=read(),b=read(),c=read(); add(a,b,c); add(b,a,c); } tot=n; mx[0]=INF; find_rt(1,0); solve(rt); if(ans==INF) printf("-1"); else printf("%d",ans); return 0; }

P4149 [IOI2011]Race