loj2341「WC2018」即時戰略(隨機化,LCT/動態點分治)
阿新 • • 發佈:2020-07-14
loj2341「WC2018」即時戰略(隨機化,LCT/動態點分治)
題解時間
對於 $ datatype = 3 $ 的資料,explore操作次數只有 $ n+log n $ 。
毫無疑問無論什麼時候已經探索的區域都是一條鏈。
每當要探索出一個新點,不是在左側就是在右側。
最劣情況下呼叫次數為 $ 2n $ 。
只要隨機打亂探索順序,額外浪費的次數類似於上升子序列長度,期望 $ logn $ ,可以解決。
而對於其餘資料,依然隨機打亂探索順序,
每次對於目標點,要點在於找到距離該點最近的已知點。
可以用動態點分治嚴格 $ nlogn $ 或LCT均攤 $ nlogn $ 。
#include<bits/stdc++.h> // #include"rts.h" int explore(int,int); using namespace std; typedef long long lint; const int N=300011; int n; bool vis[N];int lst[N]; namespace task1 { int fa[N],son[N][2]; int isroot(int x){return son[fa[x]][0]!=x&&son[fa[x]][1]!=x;} void rot(int x) { int y=fa[x],z=fa[y],xis=son[y][1]==x,yis=son[z][1]==y; if(!isroot(y)) son[z][yis]=x; fa[x]=z,fa[y]=x,fa[son[x][xis^1]]=y; son[y][xis]=son[x][xis^1],son[x][xis^1]=y; } void splay(int x) { while(!isroot(x)) { int y=fa[x],z=fa[y],xis=son[y][1]==x,yis=son[z][1]==y; if(!isroot(y)) rot(xis^yis?x:y);rot(x); } } void access(int x){for(int lst=0;x;lst=x,x=fa[x]) splay(x),son[x][1]=lst;} int anc(int x){while(!isroot(x)) x=fa[x];return x;} int pre(int x){x=son[x][0];while(son[x][1]) x=son[x][1];return x;} int nxt(int x){x=son[x][1];while(son[x][0]) x=son[x][0];return x;} void find(int ep) { int x=anc(1); while(x!=ep) { int y=explore(x,ep);if(!vis[y]) vis[y]=1,fa[y]=x,x=y; else if(y==pre(x)) x=son[x][0]; else if(y==nxt(x)) x=son[x][1]; else x=anc(y); }access(x); } void play(){for(int i=2;i<=n;i++)if(!vis[lst[i]]) find(lst[i]);} } namespace task2 { void access(int &x,int ep){while(x!=ep) vis[x=explore(x,ep)]=1;} void play() { for(int i=2,l=1,r=1,x=0;i<=n;i++)if(!vis[lst[i]]) access(vis[x=explore(r,lst[i])]?l:(vis[x]=1,r=x),lst[i]); } } void play(int n,int lim,int type) { ::n=n;srand(time(NULL));vis[1]=1; for(int i=2;i<=n;i++) lst[i]=i;random_shuffle(lst+2,lst+1+n); if(type!=3) task1::play();else task2::play(); }