BZOJ3065 带插入区间K小值
推荐学习WJMZBMR《重量平衡树和后缀平衡树在信息学奥赛中的应用》
转自hzwer:
在替罪羊树每个结点放一棵包含该子树所有结点的权值线段树,也就是平衡树套权值线段树
1、由于外层是平衡树,那么就能实现插入一个结点:找到它的位置,在根到其路径上所有结点的线段树中插入这个值
2、查询区间第K大:找到这个区间包含若干棵子树,拿出他们的根的权值线段树,一起做个二分
3、修改与插入类似
4、当外层平衡树失衡的时候重构之,将某个子树自下而上线段树合并
然后就是考验数据结构的能力了QAQ
由于内存不够,我们还需要回收垃圾,即对数组的重复使用
自己又加了许多的备注
1 #include<bits/stdc++.h> 2 #define alpha 0.75 3 using namespace std; 4 const int N=7e4+10,M=1e7+10; 5 int tmp,n,m,sz,ans,root; 6 int v[N],dfn[N],rt[N],ls[N],rs[N]; 7 struct node{ 8 int l,r,s; 9 }t[M]; 10 vector<int>rec,a,p; 11 int L=0,R=70000;//所谓权值线段树即是将点的权值作为线段树的下标 12 inline int newnode() 13 { 14 if(!rec.size()) 15 return ++sz; 16 else{ 17 int k=rec.back();rec.pop_back(); 18 return k; 19 }//循环利用 20 } 21 void reclaim(int &x) 22 { 23 if(!x)return; 24 rec.push_back(x); 25 reclaim(t[x].l);reclaim(t[x].r);//清除权值线段树 26 t[x].s=0;x=0; 27 } 28 void insert(int &k,int l,int r,int pos,int f) 29 { 30 if(!k)k=newnode(); 31 if(l==r){t[k].s+=f;return;} 32 int mid=l+r>>1; 33 if(pos<=mid)insert(t[k].l,l,mid,pos,f); 34 else insert(t[k].r,mid+1,r,pos,f); 35 t[k].s=t[t[k].l].s+t[t[k].r].s; 36 if(!t[k].s)reclaim(k);//如果这个大小没有数我们就将它回收利用 37 } 38 void build(int &k,int l,int r) 39 { 40 if(l>r)return; 41 if(l==r){ 42 k=dfn[l]; 43 insert(rt[k],L,R,v[k],1);//构造权值线段树 44 return; 45 } 46 int mid=l+r>>1;k=dfn[mid]; 47 build(ls[k],l,mid-1);build(rs[k],mid+1,r); 48 for(int i=l;i<=r;++i) 49 insert(rt[k],L,R,v[dfn[i]],1);//对每颗子树建权值线段树 50 } 51 void del(int &x) 52 { 53 if(!x)return; 54 reclaim(rt[x]); 55 del(ls[x]);p.push_back(x);del(rs[x]);//清除每个点的权值线段树 56 x=0; 57 } 58 void rebuild(int &x) 59 { 60 del(x);int s1=p.size(); 61 for(int i=1;i<=s1;++i) 62 dfn[i]=p[i-1];//中序遍历后将值重新分配 63 build(x,1,s1);//暴力重构 64 p.clear(); 65 } 66 int modify(int k,int x,int w) 67 { 68 insert(rt[k],L,R,w,1);//修改路径上每颗子树 69 int LS=t[rt[ls[k]]].s,last; 70 if(LS+1==x){last=v[k];v[k]=w;} 71 else if(LS>=x)last=modify(ls[k],x,w); 72 else last=modify(rs[k],x-LS-1,w); 73 insert(rt[k],L,R,last,-1);//将原来的删去 74 return last; 75 } 76 void get(int k,int l,int r)//此时l,r不同于平时的区间而是指的是在k的线段树中大小为l~r这一段 77 { 78 int LS=t[rt[ls[k]]].s,SUM=t[rt[k]].s; 79 if(l==1&&r==SUM)//如果指的整颗子树 80 {a.push_back(rt[k]);return;} 81 if(l<=LS+1&&r>=LS+1)p.push_back(v[k]);//如果单独是这一个点 82 if(r<=LS)get(ls[k],l,r); 83 else if(l>LS+1)get(rs[k],l-LS-1,r-LS-1); 84 else{//分跨两边,此时中间点已经拿走 85 if(l<=LS)get(ls[k],l,LS); 86 if(SUM>LS+1)get(rs[k],1,r-LS-1); 87 } 88 } 89 int query(int l,int r,int k) 90 { 91 get(root,l,r);//找出这区间中的所有子树 92 int s1=a.size(),s2=p.size();k--; 93 l=L,r=R; 94 while(l<r) 95 { 96 int mid=l+r>>1,sum=0; 97 for(int i=0;i<s1;++i)sum+=t[t[a[i]].l].s; 98 for(int i=0;i<s2;++i) 99 if(p[i]>=l&&p[i]<=mid)sum++; 100 if(sum>k){ 101 for(int i=0;i<s1;++i)a[i]=t[a[i]].l; 102 r=mid; 103 } 104 else{ 105 for(int i=0;i<s1;++i)a[i]=t[a[i]].r; 106 l=mid+1;k-=sum; 107 } 108 } 109 a.clear();p.clear(); 110 return l; 111 } 112 void insert(int &k,int x,int w) 113 { 114 if(!k) 115 { 116 k=++n; 117 insert(rt[k],L,R,w,1); 118 v[k]=w; 119 return; 120 } 121 insert(rt[k],L,R,w,1); 122 int LS=t[rt[ls[k]]].s; 123 if(LS>=x)insert(ls[k],x,w); 124 else insert(rs[k],x-LS-1,w); 125 if(t[rt[k]].s*alpha>max(t[rt[ls[k]]].s,t[rt[rs[k]]].s))//满足 126 { 127 if(tmp) 128 { 129 if(ls[k]==tmp)rebuild(ls[k]); 130 else rebuild(rs[k]); 131 tmp=0; 132 } 133 } 134 else tmp=k;//不满足 135 } 136 int main() 137 { 138 scanf("%d",&n); 139 for(int i=1;i<=n;++i)scanf("%d",&v[i]); 140 for(int i=1;i<=n;++i)dfn[i]=i; 141 build(root,1,n);//构造替罪羊树 142 scanf("%d",&m);char ch[10];int x,y,k; 143 for(int i=1;i<=m;++i) 144 { 145 scanf("%s",ch); 146 scanf("%d%d",&x,&y);x^=ans;y^=ans; 147 if(ch[0]=='Q') 148 { 149 scanf("%d",&k);k^=ans; 150 ans=query(x,y,k);printf("%d\n",ans); 151 } 152 else if(ch[0]=='M') 153 { 154 modify(root,x,y); 155 } 156 else 157 { 158 tmp=0;insert(root,x-1,y); 159 if(tmp){tmp=0;rebuild(root);}//重构整颗树 160 } 161 } 162 return 0; 163 }
生命中真正重要的不是你遭遇了什么,而是你记住了哪些事,又是如何铭记的。