题面ios
题解:首先发现对于datatype=3的点,容许的询问次数是\(O(n+log_n)\)级别的。那么先考虑树是一条链的状况。考虑当前咱们已经找到了这个链的中间一段,咱们称其左端点为\(l\),右端点为\(r\),那么此时有意义的询问显然是\((l/r,x)\),\(x\)是一个未被探索到的点。咱们称一次询问“失败”,当且仅当询问的结果是一个已知的节点。若是一次询问(假设是\((l,x)\))成功了,那么从\(l\)一直往左拓展,咱们就能找到\(l\)到\(x\)这条链上的全部点。不然\(x\)必定在\(r\)那边,咱们向右拓展,也能找到一条链上的全部点。考虑毒瘤出题人可能会能够造数据卡你,咱们把\([2,n]\)这个序列random_shuffle一下,这样,若是当前还有\(m\)个点没被探索到,一次探索过程指望能找到\(\frac{m}{2}\)个节点,因此只会有logn次探索过程。因为每次探索过程只有在第一次探索时可能会失败,因此失败的指望次数是\(O(logn)\)的,能够经过。c++
再考虑datatype=1,2的状况,此时的操做次数限制大概是\(O(nlogn)\)。也就是说,每次须要只用log次询问就找到一个未被访问的点。先考虑一个\(O(n^2)\)的暴力作法:每次都从1号节点开始探索。对于datatype=2的状况,这样作是正确的,由于这棵有根树的最大深度是\(O(logn)\)级别的。那么考虑对于普通状况,如何将比较深的树转化为一棵比较“均衡”的树。dom
想到作动态点分治时,点分树的深度是\(O(logn)\)的,那么考虑对于已经经过探索获得的边,建出它的点分树。这样,每次只须要最多\(O(logn)\)次询问,就能找到至少一个未被访问的点。ide
可是考虑将新找到的点加入后,点分树的平衡性可能会被破坏,但咱们又不可能每次都重构点分树,那么考虑替罪羊树的思想,定义一个alpha值,对于每一个分治中心\(u\),若是在点分树上存在一个儿子的\(siz>siz_u\times alpha\),咱们就重构这个分治中心的点分树(只修改点分树上的一个子树)。那么咱们每次找到一些新节点并加入点分树后,跳一下father,找到点分树上深度最小的须要重构的点\(u\),进行重构便可。测试
固然用lct也能够维护这个东西。ui
操做次数和时间复杂度都是\(O(nlogn)\)的,至于重构的复杂度证实,能够搜一下有关替罪羊树的博客。spa
代码:(wc的全部测试点都过了,log上死活过不去extra test,无限d造数据人)code
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<map> #include<cassert> #include "rts.h" using namespace std; #define F(x,y,z) for(int x=y;x<=z;x++) #define FOR(x,y,z) for(int x=y;x>=z;x--) #define I void #define IN int #define C(x,y) memset(x,y,sizeof(x)) #define STS system("pause") typedef double db; const int INF=1e9+7; //IN explore(int,int); map<int,int>mp[303000]; int N,maxi,Son,lim,cano[300300],num,siz[303000],mx[303000],head[303000],v[303000],vis[303000],tot,sz[303000],fa[303000],xu[303000]; int root,rot; I solve3(int n){ F(i,2,n)xu[i]=i;sort(xu+2,xu+1+n);int L,R;L=R=1; v[1]=1; F(cur,2,n){ if(v[xu[cur]])continue; int i=xu[cur],p=explore(L,i),now; if(!v[p]){ now=p; while(1){ v[now]=1;if(now==i)break; now=explore(now,i); } L=i; } else{ now=explore(R,i); while(1){ v[now]=1;if(now==i)break; now=explore(now,i); } R=i; } } } struct E{ int to,nt; }e[606000]; #define T e[k].to I add(int x,int y){e[++tot].to=y;e[tot].nt=head[x];head[x]=tot;} I findroot(int &roo,int x,int fat){ siz[x]=1;mx[x]=0; for(int k=head[x];k!=-1;k=e[k].nt){ if(vis[T]||T==fat)continue; findroot(roo,T,x); siz[x]+=siz[T];mx[x]=max(mx[x],siz[T]); } mx[x]=max(mx[x],N-siz[x]); if(mx[x]<maxi)roo=x,maxi=mx[x]; } I getroot(int &roo,int pos,int sum){ maxi=INF;N=sum;findroot(roo,pos,0); } I D_1(int x,int fat){ siz[x]=1; for(int k=head[x];k!=-1;k=e[k].nt){ if(T==fat||vis[T])continue; D_1(T,x);siz[x]+=siz[T]; } } I divided(int x,int fat){ // cerr<<"#"<<x<<" "<<fat<<endl; fa[x]=fat;mp[x].clear(); D_1(x,fat);vis[x]=1;sz[x]=siz[x]; for(int k=head[x];k!=-1;k=e[k].nt){ if(T==fat||vis[T])continue; int rt;getroot(rt,T,siz[T]); mp[x][T]=rt;divided(rt,x); } } I D_2(int x,int fat){ vis[x]=0;num++; for(int k=head[x];k!=-1;k=e[k].nt){ if(cano[T]){if(T==lim)Son=x;continue;}if(T==fat)continue; D_2(T,x); } } I rebuild(int n){ int pos=lim=fa[n];while(pos)cano[pos]=1,pos=fa[pos]; // cerr<<"!"<<n<<endl; num=0;D_2(n,0); int rt;getroot(rt,n,num);pos=fa[n];while(pos)cano[pos]=0,pos=fa[pos]; if(n==root){root=rt;divided(root,0);} else{ mp[lim][Son]=rt;assert(rt);divided(rt,lim); } } IN ck(int x,int y){ db w=x*0.6;if(w<y*1.0)return 1; return 0; }/**/ void play(int n,int _T,int type){ /**/if(type==3)return solve3(n),void(); F(i,2,n)xu[i]=i;random_shuffle(xu+2,xu+n+1); C(head,-1);tot=-1; root=v[1]=sz[1]=vis[1]=1; F(cur,2,n){ if(v[xu[cur]])continue; // cerr<<"@"<<xu[cur]<<endl; int i=xu[cur],p=root,d; while(1){ // cerr<<"@"; // assert(p); d=explore(p,i); if(!v[d])break; p=mp[p][d]; } int cnt=1,_son=d,now=d,sn=0;v[d]=1;add(p,d);add(d,p); while(now^i){ // cerr<<"#"; d=explore(now,i);cnt++;N++; add(now,d);add(d,now);v[d]=1; now=d; } getroot(rot,now,cnt);mp[p][_son]=rot;divided(rot,p); while(p^root){ // cout<<"$"; sz[p]+=cnt;if(ck(sz[fa[p]]+cnt,sz[p]))sn=fa[p]; p=fa[p]; } sz[root]+=cnt; if(sn)rebuild(sn); // F(j,1,n)if(v[j])cerr<<"%"<<j<<" "<<fa[j]<<endl; } } //g++ grader.cpp rts.cpp -o rts -O2