洛谷 P3380 【模板】二逼平衡树(树套树)

洛谷 P3380 【模板】二逼平衡树(树套树)

线段树套treap:

就是线段树每个节点放一个treap。建树复杂度应该是$n log n$,操作1,3,4,5的复杂度是$(log n)^2$,操作2的复杂度是$(log n)^3$。

操作3:找到线段树的对应叶子节点后找到要删除的值,在回溯的时候更新线段树相关的每一个节点(在treap中去掉要删除的值,再加入要加入的值)

操作1:将操作转化为统计(这个区间[l,r]内小于x的数的个数)+1。那么通过线段树将区间分解,然后对分解出的每一个区间对应的treap求小于x的数的个数,最后将这些答案加起来再加一得到最终答案。

操作4:通过线段树将区间分解,然后对分解出的每一个区间对应的treap求x的前驱,取这些前驱的最大值。

操作5:通过线段树将区间分解,然后对分解出的每一个区间对应的treap求x的后继,取这些后继的最小值。

操作2:咋一看似乎没什么好方法。看题解,可以将操作转化为找出这个区间[l,r]内最小的x,使得区间内小于x的数不少于k-1个(即x的排名不低于k)。二分答案即可。

  1 #include<cstdio>
  2 #include<algorithm>
  3 using namespace std;
  4 #define MAXI 2147483647
  5 #define lc (num<<1)
  6 #define rc (num<<1|1)
  7 #define mid ((l+r)>>1)
  8 int rand1()
  9 {
 10     static int x=471;
 11     return x=(48271LL*x+1)%2147483647;
 12 }
 13 struct Node
 14 {
 15     Node* ch[2];
 16     int r;//优先级
 17     int v;//value
 18     int size;//维护子树的节点个数
 19     int num;//当前数字出现次数
 20     int cmp(int x) const//要在当前节点的哪个子树去查找,0左1右
 21     {
 22         if(x==v)    return -1;
 23         return v<x;
 24     }
 25     void upd()
 26     {
 27         size=num;
 28         if(ch[0]!=NULL)    size+=ch[0]->size;
 29         if(ch[1]!=NULL)    size+=ch[1]->size;
 30     }
 31 }nodes[3001000];
 32 int mem;
 33 void rotate(Node* &o,int d)
 34 {
 35     Node* t=o->ch[d^1];o->ch[d^1]=t->ch[d];t->ch[d]=o;
 36     o->upd();t->upd();
 37     o=t;
 38 }
 39 Node* getnode(){ return &nodes[mem++];}
 40 void insert(Node* &o,int x)
 41 {
 42     if(o==NULL)
 43     {
 44         o=getnode();o->ch[0]=o->ch[1]=NULL;
 45         o->v=x;o->r=rand1();o->num=1;
 46     }
 47     else
 48     {
 49         if(o->v==x)    ++(o->num);
 50         else
 51         {
 52             int d=o->v < x;
 53             insert(o->ch[d],x);
 54             if(o->r < o->ch[d]->r)    rotate(o,d^1);
 55         }
 56     }
 57     o->upd();
 58 }
 59 void remove(Node* &o,int x)
 60 {
 61     int d=o->cmp(x);
 62     if(d==-1)
 63     {
 64         if(o->num > 0)
 65         {
 66             --(o->num);
 67         }
 68         if(o->num == 0)
 69         {
 70             if(o->ch[0]==NULL)    o=o->ch[1];
 71             else if(o->ch[1]==NULL)    o=o->ch[0];
 72             else
 73             {
 74                 int d2=o->ch[1]->r < o->ch[0]->r;
 75                 rotate(o,d2);
 76                 remove(o->ch[d2],x);
 77             }
 78         }
 79     }
 80     else    remove(o->ch[d],x);
 81     if(o!=NULL)    o->upd();
 82 }
 83 bool find(Node* o,int x)
 84 {
 85     int d;
 86     while(o!=NULL)
 87     {
 88         d=o->cmp(x);
 89         if(d==-1)    return 1;
 90         else o=o->ch[d];
 91     }
 92     return 0;
 93 }
 94 int kth(Node* o,int k)
 95 {
 96     if(o==NULL||k<=0||k > o->size)    return 0;
 97     int s= o->ch[0]==NULL ? 0 : o->ch[0]->size;
 98     if(k>s&&k<=s+ o->num)    return o->v;
 99     else if(k<=s)    return kth(o->ch[0],k);
100     else    return kth(o->ch[1],k-s- o->num);
101 }
102 int rk(Node* o,int x)
103 {
104     if(o==NULL)    return 0;
105     int r=o->ch[0]==NULL ? 0 : o->ch[0]->size;
106     if(x==o->v)    return r;
107     else    if(x<o->v)    return rk(o->ch[0],x);
108     else    return r+ o->num +rk(o->ch[1],x);
109 }
110 int pre(Node* o,int x)
111 {
112     if(o==NULL)    return -MAXI;
113     int d=o->cmp(x);
114     if(d<=0)    return pre(o->ch[0],x);
115     else    return max(o->v,pre(o->ch[1],x));
116 }
117 int nxt(Node* o,int x)
118 {
119     if(o==NULL)    return MAXI;
120     int d=o->cmp(x);
121     if(d!=0)    return nxt(o->ch[1],x);
122     else    return min(o->v,nxt(o->ch[0],x));
123 }
124 Node* root[200100];
125 int x,L,R,k,d,n,m;//所有操作的操作区间用[L,R]表示而不是[l,r]
126 int a[50010];
127
128
129 int rk1(int l,int r,int num)//返回区间内小于x的数的个数
130 {
131     if(L<=l&&r<=R)
132     {
133         return rk(root[num],x);
134     }
135     int ans=0;
136     if(L<=mid)    ans+=rk1(l,mid,lc);
137     if(mid<R)    ans+=rk1(mid+1,r,rc);
138     return ans;
139 }
140 int pre1(int l,int r,int num)
141 {
142     if(L<=l&&r<=R)
143     {
144         return pre(root[num],x);
145     }
146     int ans=-2147483647;
147     if(L<=mid)    ans=max(ans,pre1(l,mid,lc));
148     if(mid<R)    ans=max(ans,pre1(mid+1,r,rc));
149     return ans;
150 }
151 int nxt1(int l,int r,int num)
152 {
153     if(L<=l&&r<=R)
154     {
155         return nxt(root[num],x);
156     }
157     int ans=2147483647;
158     if(L<=mid)    ans=min(ans,nxt1(l,mid,lc));
159     if(mid<R)    ans=min(ans,nxt1(mid+1,r,rc));
160     return ans;
161 }
162 void change(int l,int r,int num)
163 {
164     if(l<r)
165     {
166         if(k<=mid)    change(l,mid,lc);
167         else    change(mid+1,r,rc);
168     }
169     else
170     {
171         d=kth(root[num],1);
172     }
173     remove(root[num],d);
174     insert(root[num],x);
175 }
176 void build(int l,int r,int num)
177 {
178     for(int i=l;i<=r;i++)    insert(root[num],a[i]);
179     if(l<r)
180     {
181         build(l,mid,lc);
182         build(mid+1,r,rc);
183     }
184 }
185 int main()
186 {
187     int i,idx,l,r;
188     scanf("%d%d",&n,&m);
189     for(i=1;i<=n;i++)    scanf("%d",&a[i]);
190     build(1,n,1);
191     for(i=1;i<=m;i++)
192     {
193         scanf("%d",&idx);
194         if(idx==1)
195         {
196             scanf("%d%d%d",&L,&R,&x);
197             printf("%d\n",rk1(1,n,1)+1);
198         }
199         else if(idx==2)
200         {
201             scanf("%d%d%d",&L,&R,&k);
202             l=-1;r=100000000;
203             while(l<r-1)
204             {
205                 x=(l+r)/2;
206                 if(rk1(1,n,1)+1<=k)    l=x;
207                 else    r=x;
208             }
209             x=r;printf("%d\n",pre1(1,n,1));;
210         }
211         else if(idx==3)
212         {
213             scanf("%d%d",&k,&x);
214             change(1,n,1);
215         }
216         else if(idx==4)
217         {
218             scanf("%d%d%d",&L,&R,&x);
219             printf("%d\n",pre1(1,n,1));
220         }
221         else if(idx==5)
222         {
223             scanf("%d%d%d",&L,&R,&x);
224             printf("%d\n",nxt1(1,n,1));
225         }
226     }
227     return 0;
228 }

原文地址:https://www.cnblogs.com/hehe54321/p/8436996.html

时间: 2024-10-09 21:45:58

洛谷 P3380 【模板】二逼平衡树(树套树)的相关文章

bzoj 3196/ Tyvj 1730 二逼平衡树 (线段树套平衡树)

3196: Tyvj 1730 二逼平衡树 Time Limit: 10 Sec  Memory Limit: 128 MB[Submit][Status][Discuss] Description 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:1.查询k在区间内的排名2.查询区间内排名为k的值3.修改某一位值上的数值4.查询k在区间内的前驱(前驱定义为小于x,且最大的数)5.查询k在区间内的后继(后继定义为大于x,且最小的数) Input 第一行两个数 n,

BZOJ3196 二逼平衡树 ZKW线段树套vector(滑稽)

我实在是不想再打一遍树状数组套替罪羊树了... 然后在普通平衡树瞎逛的时候找到了以前看过vector题解 于是我想:为啥不把平衡树换成vector呢??? 然后我又去学了一下ZKW线段树 就用ZKW线段树套vector水过啦!!! 每个ZKW线段树的节点保存一个vector 操作1在分出的vector上查询比它小的数有多少个然后相加再加1 操作2二分再上操作1 操作3修改需要修改的节点的vector 操作4在分出vector上查询前驱取最大 操作5与操作4同理 luogu主站5772ms上卡过,

BZOJ3196 二逼平衡树 【线段树套平衡树】

题目 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作: 1.查询k在区间内的排名 2.查询区间内排名为k的值 3.修改某一位值上的数值 4.查询k在区间内的前驱(前驱定义为小于x,且最大的数) 5.查询k在区间内的后继(后继定义为大于x,且最小的数) 输入格式 第一行两个数 n,m 表示长度为n的有序序列和m个操作 第二行有n个数,表示有序序列 下面有m行,opt表示操作标号 若opt=1 则为操作1,之后有三个数l,r,k 表示查询k在区间[l,r]的排名 若

3196. 二逼平衡树【线段树套splay】

Description 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作: 1.查询k在区间内的排名 2.查询区间内排名为k的值 3.修改某一位值上的数值 4.查询k在区间内的前驱(前驱定义为小于x,且最大的数) 5.查询k在区间内的后继(后继定义为大于x,且最小的数) Input 第一行两个数 n,m 表示长度为n的有序序列和m个操作 第二行有n个数,表示有序序列 下面有m行,opt表示操作标号 若opt=1 则为操作1,之后有三个数l,r,k 表示查询k在区间

洛谷3380 二逼平衡树(树套树)

题目描述 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作: 查询k在区间内的排名 查询区间内排名为k的值 修改某一位值上的数值 查询k在区间内的前驱(前驱定义为严格小于x,且最大的数,若不存在输出-2147483647) 查询k在区间内的后继(后继定义为严格大于x,且最小的数,若不存在输出2147483647) 注意上面两条要求和tyvj或者bzoj不一样,请注意 输入输出格式 输入格式: 第一行两个数 n,m 表示长度为n的有序序列和m个操作 第二行有n个数,

「luogu3380」【模板】二逼平衡树(树套树)

「luogu3380」[模板]二逼平衡树(树套树) 传送门 我写的树套树--线段树套平衡树. 线段树上的每一个节点都是一棵 \(\text{FHQ Treap}\) ,然后我们就可以根据平衡树的基本操作以及线段树上区间信息可合并的性质来实现了,具体细节看代码都懂. 参考代码: #include <algorithm> #include <cstdlib> #include <cstdio> #define rg register #define file(x) freo

[BZOJ 3196] 二逼平衡树 树状数组套主席树

3196: Tyvj 1730 二逼平衡树 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 3357  Solved: 1326[Submit][Status][Discuss] Description 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:1.查询k在区间内的排名2.查询区间内排名为k的值3.修改某一位值上的数值4.查询k在区间内的前驱(前驱定义为小于x,且最大的数)5.查询k在区间内的后继(后继定义为

BZOJ3196二逼平衡树【树套树】

3196: Tyvj 1730 二逼平衡树 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 3776  Solved: 1483 Description 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:1.查询k在区间内的排名2.查询区间内排名为k的值3.修改某一位值上的数值4.查询k在区间内的前驱(前驱定义为小于x,且最大的数)5.查询k在区间内的后继(后继定义为大于x,且最小的数) Input 第一行两个数 n

BZOJ 3196: Tyvj 1730 二逼平衡树( 树套树 )

这道题做法应该很多吧.... 我用了线段树套treap.... -------------------------------------------------------------------------------------------------------------- #include<cstdio> #include<algorithm> #include<cstring> #include<cstdlib> #include<ios