替罪羊树
通过子树大小和树大小的关系进行重构,复杂度均摊logn。
bzoj3600 没有人的算术
题目大意:一开始只有0,每次可以把两个数x,y组成(x,y)的形式作为新数,数(a,b)(x,y)的比较以第一维为第一关键字、第二维为第二关键字。操作有:把l和r上的数组成新数赋给k,求[l,r]中最大数的下标(数相同时取小下标)。
思路:考虑如果维护数的信息,可以用重量平衡树,用替罪羊树,对每个数维护一个vi,vi表示平衡树上区间的中点(rt的区间是[0,1],左子树的是[l,mid],右子树的是[mid,r]),插入的时候找到深度最浅的那个点暴力重构。查询就是在线段树上查区间最值。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define N 100005 #define M 500005 #define alp 0.75 #define LD double #define eps 1e-9 using namespace std; char chin(){ char ch=getchar(); while(ch!='C'&&ch!='Q') ch=getchar(); return ch;} int in(){ char ch=getchar();int x=0; while(ch<'0'||ch>'9') ch=getchar(); while(ch>='0'&&ch<='9'){ x=x*10+ch-'0';ch=getchar(); }return x;} int cmp(LD x,LD y){ if (x-y>eps) return 1; if (y-x>eps) return -1; return 0;} struct tree{int mx,mp;}tr[N<<2]; LD vi[M]; struct point{ int x,y; bool operator==(const point&xx)const{return x==xx.x&&y==xx.y;} bool operator<(const point&xx)const{ return (cmp(vi[x],vi[xx.x])==0 ? cmp(vi[y],vi[xx.y])<0 : cmp(vi[x],vi[xx.x])<0);} }bi[M]; struct use{ int l,r,sz,po;LD le,re; bool operator<(const use&x)const{return bi[po]<bi[x.po];} }tp[M],zh[M]; int ai[N],rt,tt=0,bz=0,uf,zt,id[M],iz; tree updata(tree x,tree y){ tree c; if (cmp(vi[x.mx],vi[y.mx])>=0){ c.mx=x.mx;c.mp=x.mp; }else{c.mx=y.mx;c.mp=y.mp;} return c;} void build(int i,int l,int r){ if (l==r){tr[i]=(tree){ai[l],l};return;} int mid=(l+r)>>1; build(i<<1,l,mid);build(i<<1|1,mid+1,r); tr[i]=updata(tr[i<<1],tr[i<<1|1]); } void tch(int i,int l,int r,int x){ if (l==r){tr[i]=(tree){ai[l],l};return;} int mid=(l+r)>>1; if (x<=mid) tch(i<<1,l,mid,x); else tch(i<<1|1,mid+1,r,x); tr[i]=updata(tr[i<<1],tr[i<<1|1]); } tree ask(int i,int l,int r,int ll,int rr){ if (ll<=l&&r<=rr) return tr[i]; int mid=(l+r)>>1;tree c1,c2; bool f1,f2;f1=f2=false; if (ll<=mid){f1=true;c1=ask(i<<1,l,mid,ll,rr);} if (rr>mid){f2=true;c2=ask(i<<1|1,mid+1,r,ll,rr);} if (f1&&f2) return updata(c1,c2); if (f1) return c1; return c2;} void upd(int u){tp[u].sz=tp[tp[u].l].sz+1+tp[tp[u].r].sz;} bool bal(int u){ if (cmp((LD)max(tp[tp[u].l].sz,tp[tp[u].r].sz)*1.,alp*(LD)tp[u].sz)>0) return false; return true; } void ins(int &u,int k,point x,LD l,LD r){ if (!u){ bi[++bz]=x;ai[k]=bz; tp[u=++tt]=(use){0,0,1,bz,l,r}; vi[bz]=(l+r)/2.; return; }if (bi[tp[u].po]==x){ai[k]=tp[u].po;return;} LD mid=(l+r)/2.; if (x<bi[tp[u].po]) ins(tp[u].l,k,x,l,mid); else ins(tp[u].r,k,x,mid,r); upd(u); if (!bal(tp[u].l)||!bal(tp[u].r)) uf=u; } void dfs(int u){ if (!u) return; dfs(tp[u].l); zh[++zt]=tp[u];id[++iz]=u; dfs(tp[u].r); } void reb(int &u,int l,int r,LD ll,LD rr){ if (l>r){u=0;return;} int mid=(l+r)>>1;LD mm=(ll+rr)/2.; tp[u=id[iz--]]=zh[mid]; vi[tp[u].po]=mm; tp[u].le=ll;tp[u].re=rr; reb(tp[u].l,l,mid-1,ll,mm); reb(tp[u].r,mid+1,r,mm,rr); upd(u); } void rebuild(int &u){iz=zt=0;dfs(u);reb(u,1,zt,tp[u].le,tp[u].re);} int main(){ int i,n,m,l,r,k;char ch; n=in();m=in(); for (i=1;i<=n;++i) ai[i]=1; build(1,1,n); bi[bz=1]=(point){0,0}; tp[0]=(use){0,0,0,0,0.,0.}; tp[rt=tt=1]=(use){0,0,1,1,0.,1.}; for (i=1;i<=m;++i){ ch=chin();l=in();r=in(); if (ch=='C'){ k=in();uf=0; ins(rt,k,(point){ai[l],ai[r]},0.,1.); if (uf){ if (!bal(tp[uf].l)) rebuild(tp[uf].l); if (!bal(tp[uf].r)) rebuild(tp[uf].r); }else if (!bal(rt)) rebuild(rt); tch(1,1,n,k); }else{ tree cc=ask(1,1,n,l,r); printf("%d\n",cc.mp); } } }