題面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