【UOJ#349】[WC2018] 即時戰略
阿新 • • 發佈:2019-03-09
fin 瓶頸 bool uil dom add head 直接 pro
題目鏈接
題意
一開始已知一號點。
每次可以選定一個已知點和一個未知點,然後交互庫會返回從已知點出發到達未知點路徑上的第二個點。
要求在有限步之內知道每一個點。
次數要求:
鏈的情況要求 \(O(n)\)
其余是 \(O(nlogn)\)
Sol
首先是鏈的情況,記錄當前左右端點不斷往後探索即可。
然後是樹,初始想法肯定就是不斷叠代,最壞情況是 \(O(n^2)\) 的。
我們的瓶頸在於如果樹的深度比較大,我們叠代的時候來回走了很多個圈就不好處理。
那麽我們很容易想到用點分樹來優化我們叠代的過程。
於是動態維護點分樹即可。
每次新加一個點的時候直接加入,向上更新點分樹祖先的 \(size\) ,設定一個平衡因子,當當前子樹大小過大時就把當前子樹暴力重構一下。記錄每一個點在點分樹中的深度就很好做了。
code:
#include<bits/stdc++.h> #include "rts.h" using namespace std; const int N=3e5+10; namespace TP3{ int n;int lnow,rnow;bool del[N];int S[N]; void work(int _n){ n=_n; lnow=rnow=1;for(int i=1;i<n;++i) S[i]=i+1;del[1]=1; srand(time(NULL));random_shuffle(S+1,S+n); for(int i=1;i<n;++i){ int now=lnow;bool f=0; while(!del[S[i]]) { int v=explore(now,S[i]); if(del[v]) now=rnow,f=1; else { del[v]=1;now=v; if(f==0) lnow=now; else rnow=now; } } if(rand()&1) swap(lnow,rnow); } return; } } namespace Sol{ int n; typedef double db; const db alpha=0.7; struct edge{int to,next;}a[N<<1]; int head[N],cnt=0; inline void add(int x,int y){a[++cnt]=(edge){y,head[x]};head[x]=cnt;} int fa[N],vis[N],size[N],f[N],que[N],had[N],mark[N],sz[N],dep[N]; int rt;int now,SZ,RT,UP; void Find(int u,int fr){ sz[u]=1,f[u]=0; for(int v,i=head[u];i;i=a[i].next){ v=a[i].to;if(v==fr||vis[v]) continue; Find(v,u);sz[u]+=sz[v]; f[u]=max(f[u],sz[v]); } f[u]=max(f[u],SZ-sz[u]); if(!RT||(f[u]<f[RT])) RT=u; } void Build(int u,int fr){ fa[u]=fr;size[u]=1;dep[u]=dep[fr]+1;vis[u]=1; for(int v,i=head[u];i;i=a[i].next){ v=a[i].to;if(dep[v]<UP||vis[v]) continue; RT=0;SZ=sz[v];Find(v,u); int To=RT;Build(To,u); size[u]+=size[To]; } return; } void Clear(int u,int fa){ vis[u]=0,mark[u]=0; for(int v,i=head[u];i;i=a[i].next){v=a[i].to;if(v==fa||dep[v]<UP) continue;Clear(v,u);} return; } inline void Rebuild(int u){// 重構子樹 SZ=size[u];UP=dep[u];RT=0; Clear(u,0);Find(u,0);if(rt==u) rt=RT; Build(RT,fa[u]); return; } void Maintain(int u){// 向上更新點分樹 size 並判斷重構 if(!fa[u]) {if(mark[u]) Rebuild(u);return;} ++size[fa[u]]; if(size[fa[u]]*alpha<size[u]) mark[fa[u]]=1; Maintain(fa[u]); if(mark[u]) Rebuild(u);// 找到最上面需要重構的點 return; } void work(int _n){ n=_n; for(int i=1;i<n;++i) que[i]=i+1; srand(time(NULL)); random_shuffle(que+1,que+n); had[1]=size[1]=vis[1]=rt=1,dep[1]=1; int tot=1; for(int i=1;i<n;++i) { now=rt; while(!had[que[i]]){ int p=explore(now,que[i]); if(had[p]) { while(now!=fa[p]) p=fa[p];now=p; } else { ++tot;had[p]=1; add(now,p),add(p,now); fa[p]=now,size[p]=1,vis[p]=1,dep[p]=dep[now]+1; Maintain(p);now=p; } } } } } void play(int n, int T, int dataType) { if(dataType==3) TP3::work(n); else Sol::work(n); }
【UOJ#349】[WC2018] 即時戰略