BZOJ3065 带插入区间K小值

Posted 大奕哥&VANE

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 }

 

以上是关于BZOJ3065 带插入区间K小值的主要内容,如果未能解决你的问题,请参考以下文章

[BZOJ3065]带插入区间K小值 解题报告 替罪羊树+值域线段树

Bzoj3065 带插入区间K小值

bzoj 3065 带插入区间k小值

BZOJ3065 带插入区间K小值

[BZOJ3065]带插入区间K小值

[bzoj3065] 带插入区间第k小值 [重量平衡树套线段树]