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;
}

  

posted @ 2019-05-12 03:59  Claris  阅读(985)  评论(0编辑  收藏  举报