[BZOJ]3065: 带插入区间K小值
题目大意:一个长度为n的序列,支持三种操作:1.查询区间k小值;2.修改一个元素;3.插入一个元素;强制在线。(n<=35000,插入操作数<=35000,修改操作数<=70000,查询操作数<=70000,0<=数字大小<=70000,4个点,总时限60s)
思路:做法比较多,我写的是权值线段树套平衡树,平衡树内维护该权值区间内各元素位置,每次查询从线段树根结点开始左右走就可以了。现在问题是如何解决带插入的情况下快速比较两个位置标号的前后关系,我们可以用替罪羊树维护,我们给每个位置标号定个rank区间,例如根定为[0,1e18],定其rank值为中点5*1e17,那么他左儿子的rank区间为[0,5*1e17],右儿子为[5*1e17,1e18],这样我们每次只要O(1)就可以比较两个位置标号的前后关系,我们用double存这个rank,如果树深度太高,rank精度可能不够,我们重构替罪羊树时一并重构rank即可解决。总复杂O((n+q)*log^2),这个代码跑了28s左右。
#include<cstdio> inline int read() { int x;char c; while((c=getchar())<'0'||c>'9'); for(x=c-'0';(c=getchar())>='0'&&c<='9';)x=(x<<3)+(x<<1)+c-'0'; return x; } #define MN 70000 #define N 131072 #define LG 17 #define ND LG*MN struct sgt { static const double alpha=0.75; int a[MN+5],rt,lc[MN+5],rc[MN+5],s[MN+5],cnt,*rb; double lh[MN+5],rh[MN+5],rk[MN+5]; int build(int l,int r,double lx,double rx) { if(l>r)return 0; int mid=l+r>>1,x=a[mid];s[x]=r-l+1; rk[x]=((lh[x]=lx)+(rh[x]=rx))/2; lc[x]=build(l,mid-1,lx,rk[x]); rc[x]=build(mid+1,r,rk[x],rx); return x; } void ins(int*x,int z,int k,double lx,double rx) { if(!*x){rk[*x=z]=((lh[z]=lx)+(rh[z]=rx))/2;s[z]=1;return;} ++s[*x]; if(k<s[lc[*x]]+2)ins(lc+*x,z,k,lx,rk[*x]); else ins(rc+*x,z,k-s[lc[*x]]-1,rk[*x],rx); if(s[lc[*x]]>s[*x]*alpha||s[rc[*x]]>s[*x]*alpha)rb=x; } void dfs(int x){if(x)dfs(lc[x]),a[++cnt]=x,dfs(rc[x]);} void insert(int x,int k) { rb=0;ins(&rt,x,k,1,1e18); if(rb)cnt=0,dfs(*rb),*rb=build(1,cnt,lh[*rb],rh[*rb]); } int find(int k) { for(int x=rt;;) if(k<=s[lc[x]])x=lc[x]; else if(k-=s[lc[x]]+1)x=rc[x]; else return x; } }sgt; int a[MN+5],rt[N*2+5],d[ND+5]; namespace treap { int fa[ND+5],c[ND+5][2],s[ND+5],p[ND+5]; inline int cmp(int x,int y){return sgt.rk[d[x]]<sgt.rk[d[y]];} inline int ran() { static int x=23333; return x^=x<<13,x^=x>>17,x^=x<<5; } inline void up(int x){s[x]=s[c[x][0]]+s[c[x][1]]+1;} void rotate(int x) { int f=fa[x],ff=fa[f],l=c[f][1]==x,r=l^1; fa[f]=x;fa[x]=ff;fa[c[x][r]]=f; (ff>0?c[ff][c[ff][1]==f]:rt[-ff])=x;c[f][l]=c[x][r];c[x][r]=f; up(f);up(x); } void ins(int&x,int f,int z) { if(!x) { fa[x=z]=f;s[z]=1;p[z]=ran(); while(fa[z]>0&&p[z]>p[fa[z]])rotate(z); return; } ++s[x];ins(c[x][cmp(x,z)],x,z); } void del(int x) { while(c[x][0]||c[x][1])rotate(c[x][p[c[x][0]]<p[c[x][1]]]); if(fa[x]<0){rt[-fa[x]]=0;return;} c[fa[x]][c[fa[x]][1]==x]=0; for(int i=x;(i=fa[i])>0;)up(i); } int sum(int x,int z,int f) { if(!x)return 0; if(f?cmp(x,z):!cmp(z,x))return s[c[x][0]]+1+sum(c[x][1],z,f); return sum(c[x][0],z,f); } }; void ins(int k,int x) { for(int i=(k+=N,0);i<LG;++i,k>>=1) if(~k&1)treap::ins(rt[k],-k,i*MN+x); } void del(int k,int x) { for(int i=(k+=N,0);i<LG;++i,k>>=1) if(~k&1)treap::del(i*MN+x); } int query(int k,int l,int r,int x) { if(k>=N)return k-N; int lk=treap::sum(rt[k<<1],r,0)-treap::sum(rt[k<<1],l,1); if(x<=lk)return query(k<<1,l,r,x); return query(k<<1|1,l,r,x-lk); } int main() { int n=read(),i,j,x,y;char s[5]; for(i=1;i<=n;++i)sgt.a[i]=i; sgt.rt=sgt.build(1,n,1,1e18); for(i=0;i<LG;++i)for(j=1;j<=MN;++j)d[i*MN+j]=j; treap::p[0]=0x80000000; for(i=1;i<=n;++i)ins(a[i]=read(),i); for(i=read(),j=0;i--;) { scanf("%s",s);x=read()^j;y=read()^j; if(s[0]=='Q')printf("%d\n",j=query(1,sgt.find(x),sgt.find(y),read()^j)); if(s[0]=='M')x=sgt.find(x),del(a[x],x),ins(a[x]=y,x); if(s[0]=='I')sgt.insert(++n,x),ins(a[n]=y,n); } }