Description
小 M 在玩一个即时战略 (Real Time Strategy) 游戏。不同于大多数同类游戏,这个游戏的地图是树形的。也就是说,地图可以用一个由 $n$ 个结点,$n - 1$ 条边构成的连通图来表示。这些结点被编号为 $1 sim n$。
每个结点有两种可能的状态:「已知的」或「未知的」。游戏开始时,只有 $1$ 号结点是已知的。
在游戏的过程中,小 M 可以尝试探索更多的结点。具体来说,小 M 每次操作时需要选择一个已知的结点 $x$,和一个不同于 $x$ 的任意结点 $y$(结点 $y$ 可以是未知的)。然后游戏的自动寻路系统会给出 $x$ 到 $y$ 的最短路径上的第二个结点 $z$,也就是从 $x$ 走到 $y$ 的最短路径上与 $x$ 相邻的结点。此时,如果结点 $z$ 是未知的,小 M 会将它标记为已知的。
这个游戏的目标是:利用至多 $T$ 次探索操作,让所有结点的状态都成为已知的。然而小 M 还是这个游戏的新手,她希望得到你的帮助。
为了让游戏过程更加容易,小 M 给你提供了这个游戏的交互库,具体见「任务描述」和「实现细节」。
另外,小 M 也提供了一些游戏的提示,具体见题目的最后一节「提示」。
Solution
做法一:
用LCT维护已经探索过的点,维护每一个点的前驱和后继,随机选择扩展某个点,假设$t= ext{explore}( ext{rand()}, ext{now})$,其中now初始时是LCT的根
会有以下几种情况:1.$t$是now的前驱或后继,这个时候可以选择跳到Splay的左儿子或右儿子,相当于在Splay上做二分;2.$t$在其他Splay上,此时直接跳跃至该Splay的根;3.$t$从未被探索过,此时直接将其加入LCT
不断重复以上过程直至探索到该随机值
复杂度是$O(n log n)$的,因为以上过程实际上是LCT中access操作的逆序
做法二:
用点分树维护已经探索过的点,仍然随机扩展某个点,每求出一个$t$就在点分树上不断向上跳父节点直至知道其在当前now的什么方向,最终到叶子时会有新点加入点分树,此时直接加入,当点分树不平衡到一定程度时重构,类似替罪羊树
时间复杂度$O(n log^2n)$
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//#include"rts.h" #include<algorithm> #include<iostream> #include<cstdlib> #include<cstdio> using namespace std; int p[300005],L[300005],R[300005],ch[300005][2],fa[300005],pos[2]={1,1}; bool vst[300005]; int explore(int ,int); bool nroot(int x){return ch[fa[x]][0]==x||ch[fa[x]][1]==x;} int getson(int x){return ch[fa[x]][1]==x;} void pushup(int x){ L[x]=R[x]=x; if(ch[x][0])L[x]=L[ch[x][0]]; if(ch[x][1])R[x]=R[ch[x][1]]; } void rotate(int x){ int y=fa[x],z=fa[y],b=getson(x),c=getson(y),a=ch[x][!b]; if(nroot(y))ch[z][c]=x; fa[x]=z,ch[x][!b]=y,fa[y]=x,ch[y][b]=a; if(a)fa[a]=y; pushup(y),pushup(x); } void splay(int x){ while(nroot(x)){ int y=fa[x]; if(nroot(y)) if(getson(y)==getson(x))rotate(y); else rotate(x); rotate(x); } } void access(int x){for(int y=0;x;x=fa[y=x])splay(x),ch[x][1]=y,pushup(x);} void link(int x,int y){fa[y]=x,pushup(x);} int find(int x){ while(nroot(x))x=fa[x]; return x; } void play(int n,int T,int dataType){ for(int i=1;i<n;i++)p[i]=i+1; random_shuffle(p+1,p+n); if(dataType==3){ for(int i=1;i<n;i++){ int now=p[i],d=rand()&1; if(vst[now])continue; int t=explore(pos[d],now); if(vst[t])t=explore(pos[d^=1],now); while(t!=now)vst[t]=true,t=explore(t,now); vst[pos[d]=now]=true; } } else{ for(int i=1;i<=n;i++)L[i]=R[i]=i; for(int i=1;i<n;i++){ int now=p[i]; if(vst[now])continue; int x=find(1); while(x!=now){ int t=explore(x,now); if(t==R[ch[x][0]])x=ch[x][0]; else if(t==L[ch[x][1]])x=ch[x][1]; else if(!vst[t]){ while(t!=now)link(x,t),x=t,vst[x]=true,t=explore(x,now); link(x,t),vst[t]=true,x=t; } else x=find(t); } access(x); } } } /* g++ grader.cpp my.cpp -o rts -O2 */