BZOJ5243 : [Lydsy2017省队十连测]绝版题
要找的就是这棵树的带权重心,以带权重心为根时每棵子树的权值和不超过总权值和的一半。
因此按$\frac{v[i]}{\sum v[i]}$的概率随机选取一个点$x$,则重心有$\frac{1}{2}$的概率落在$1$到$x$的路径上,期望随机次数为$O(1)$。
随机方式可以直接随机一个$1$到$\sum v[i]$之间的数,然后相当于找第$k$小值,线段树上二分可以做到$O(\log n)$定位。
设$sum[x]$表示$x$子树的权值和,可以用LCT打标记维护。
在表示$1$到$x$路径的Splay上找到最靠右的点$y$,满足$2sum[y]>sum[1]$;若$y$最重的儿子$z$不满足$2sum[z]>sum[1]$,则$y$是答案。
需要快速查询一个点的儿子里的$sum$的最大值,因此用set维护每个点的虚儿子即可。
一共$O(n\log n)$次虚实边切换,时间复杂度$O(n\log^2n)$。
#include<cstdio> #include<set> #include<algorithm> using namespace std; typedef unsigned int U; typedef long long ll; typedef unsigned long long ull; const int N=150010,M=524300; int n,m,lim,i,op,x,y,ans,e[300010][3],val[N],pos[N],pre;ll sum[M]; int f[N],son[N][2],tmp[N];ll sz[N],vl[N],mx[N],tag[N]; multiset<ll>T[N]; inline void read(int&a){char c;while(!(((c=getchar())>='0')&&(c<='9')));a=c-'0';while(((c=getchar())>='0')&&(c<='9'))(a*=10)+=c-'0';} inline bool isroot(int x){return !f[x]||son[f[x]][0]!=x&&son[f[x]][1]!=x;} inline void tag1(int x,ll p){ if(!x)return; tag[x]+=p; sz[x]+=p; mx[x]+=p; vl[x]+=p; } inline void pb(int x){if(tag[x])tag1(son[x][0],tag[x]),tag1(son[x][1],tag[x]),tag[x]=0;} inline void umax(ll&a,ll b){a<b?(a=b):0;} inline void up(int x){ if(!x)return; mx[x]=vl[x]=sz[x]; if(son[x][0]){ umax(mx[x],mx[son[x][0]]); vl[x]=vl[son[x][0]]; } if(son[x][1])umax(mx[x],mx[son[x][1]]); } inline void rotate(int x){ int y=f[x],w=son[y][1]==x; son[y][w]=son[x][w^1]; if(son[x][w^1])f[son[x][w^1]]=y; if(f[y]){ int z=f[y]; if(son[z][0]==y)son[z][0]=x;else if(son[z][1]==y)son[z][1]=x; } f[x]=f[y];f[y]=x;son[x][w^1]=y;up(y); } inline void splay(int x){ int s=1,i=x,y;tmp[1]=i; while(!isroot(i))tmp[++s]=i=f[i]; while(s)pb(tmp[s--]); while(!isroot(x)){ y=f[x]; if(!isroot(y)){if((son[f[y]][0]==y)^(son[y][0]==x))rotate(x);else rotate(y);} rotate(x); } up(x); } inline void access(int x){ for(int y=0;x;y=x,x=f[x]){ splay(x); if(son[x][1])T[x].insert(vl[son[x][1]]); if(son[x][1]=y)T[x].erase(T[x].find(vl[y])); up(x); } } inline void add(int x,int p){access(x);splay(x);tag1(x,p);} void build(int x,int a,int b){ if(a==b){ sum[x]=val[a]; pos[a]=x; return; } int mid=(a+b)>>1; build(x<<1,a,mid); build(x<<1|1,mid+1,b); sum[x]=sum[x<<1]+sum[x<<1|1]; } U SX=335634763,SY=873658265,SZ=192849106,SW=746126501; inline ull xorshift128(){ U t=SX^(SX<<11); SX=SY; SY=SZ; SZ=SW; return SW=SW^(SW>>19)^t^(t>>8); } inline ull myrand(){return (xorshift128()<<32)^xorshift128();} inline int getrand(){ ll k=myrand()%sum[1]+1; int x=1,a=1,b=lim,mid; while(a<b){ mid=(a+b)>>1; ll tmp=sum[x<<1]; if(k<=tmp){ b=mid; x<<=1; }else{ k-=tmp; a=mid+1; x=x<<1|1; } } return a; } inline void modify(int x,int y){ val[x]=y; x=pos[x]; sum[x]=y; for(x>>=1;x;x>>=1)sum[x]=sum[x<<1]+sum[x<<1|1]; } inline int centroid(){ for(int i=0;;i++){ int x=getrand(); if(!i&&ans)x=ans; access(x); splay(x); int y=x,ret=1; while(x){ if(sz[x]*2>sum[1])ret=x; pb(x); if(mx[son[x][1]]*2>sum[1])x=son[x][1];else x=ret==x?0:son[x][0]; if(x)y=x; } splay(y); access(ret); if(T[ret].size()==0||*T[ret].rbegin()*2<=sum[1])return ret; } } int main(){ read(m),read(val[1]); n=lim=1; sz[1]=mx[1]=vl[1]=val[1]; for(i=1;i<=m;i++){ read(op); e[i][0]=op; if(op<3)read(e[i][1]),read(e[i][2]); if(op==1)lim++; } build(1,1,lim); for(i=1;i<=m;i++){ op=e[i][0]; x=e[i][1]^ans; y=e[i][2]^ans; if(op==1){ modify(++n,y); f[n]=x; T[x].insert(0); add(n,y); }else if(op==2){ add(x,y-val[x]); modify(x,y); }else{ if(pre!=3){ int now=centroid(); ans=now; } printf("%d\n",ans); } pre=op; } return 0; }