BZOJ_1901_&_ZJU_2112_Dynamic_Rankings(主席树+树状数组/线段树+(Treap/Splay))

描述



http://www.lydsy.com/JudgeOnline/problem.php?id=1901

给出一个长度为n的数列A,有m次询问,询问分两种:1.修改某一位置的值;2.求区间[l,r]内的第k小的值.

分析



对于只有第一种询问的问题: POJ_2104_Kth(主席树)

现在要求动态.我们思考这样一个问题:把求区间第k小的问题变成求区间和值的问题,这个好解决吧?对于静态的问题,我们使用前缀和即可解决,那么对于动态的呢?使用树状数组维护前缀和.那么现在把问题变回求区间第k小值的问题.对于静态的问题,我们还是使用前缀和的思想,不过这一次每个前缀和不再是代表[1,i]的和值,而是[1,i]的一棵线段树,然后找到区间左右断点,相减即可得到答案.者可以理解为"前缀和套线段树",并且我们使用可持久化的思想大大减小空间开销.那么对于动态的问题,我们还是使用树状数组的思想,不过这一次每个点不再代表[i-lowbit(i)+1,i]的和值,而是代表[i-lowbit[i]+1,i]的一棵线段树.问题就迎刃而解了.

注意:

1.这里的每一个点代表的主席树刚开始都是由root[i](=0)建立的,之后修改的时候已有的就不需要再建立了.由于主席树的空间需求不好估计(对于这样的问题,空间的上界是(n+m)(logn*logn),但实际上远远用不到),虽然也可以写成每次修改无论之前有没有都直接复制一份,但是在空间不确定的情况下,写成前一种不容易爆炸.

 1 #include <cstdio>
 2 #include <algorithm>
 3 using namespace std;
 4
 5 const int maxn=10000+5;
 6 struct node{ int l,r,s; }t[maxn*400];
 7 struct qry{ int a,b,c; }q[maxn];
 8 int n,m,cnt,tot,num;
 9 int a[maxn],b[maxn<<1],root[maxn],L[maxn],R[maxn];
10
11 inline int lowbit(int x){ return x&(-x); }
12 void update(int l,int r,int &pos,int d,int s){
13     if(!pos)t[++cnt]=t[pos], pos=cnt;
14     t[pos].s+=s;
15     if(l==r) return;
16     int mid=(l+r)/2;
17     if(d<=mid) update(l,mid,t[pos].l,d,s);
18     else update(mid+1,r,t[pos].r,d,s);
19 }
20 void change(int x,int d,int s){ for(;x<=n;x+=lowbit(x)) update(1,num,root[x],d,s); }
21 int query(int l,int r,int k,int cl,int cr){
22     if(l==r) return l;
23     int suml=0,sumr=0;
24     for(int i=1;i<=cl;i++) suml+=t[t[L[i]].l].s;
25     for(int i=1;i<=cr;i++) sumr+=t[t[R[i]].l].s;
26     int s=sumr-suml,mid=(l+r)/2;
27     if(k<=s){
28         for(int i=1;i<=cl;i++) L[i]=t[L[i]].l;
29         for(int i=1;i<=cr;i++) R[i]=t[R[i]].l;
30         return query(l,mid,k,cl,cr);
31     }
32     else{
33         for(int i=1;i<=cl;i++) L[i]=t[L[i]].r;
34         for(int i=1;i<=cr;i++) R[i]=t[R[i]].r;
35         return query(mid+1,r,k-s,cl,cr);
36     }
37 }
38 int get_ans(int l,int r,int k){
39     int cl,cr;
40     for(cl=0;l>0;l-=lowbit(l)) L[++cl]=root[l];
41     for(cr=0;r>0;r-=lowbit(r)) R[++cr]=root[r];
42     return query(1,num,k,cl,cr);
43 }
44 int main(){
45     scanf("%d%d",&n,&m);
46     char c;
47     for(int i=1;i<=n;i++) scanf("%d",&a[i]), b[++tot]=a[i];
48     for(int i=1;i<=m;i++){
49         for(c=getchar();c<‘A‘||c>‘Z‘;c=getchar());
50         scanf("%d%d",&q[i].a,&q[i].b);
51         if(c==‘Q‘) scanf("%d",&q[i].c);
52         else b[++tot]=q[i].b;
53     }
54     sort(b+1,b+1+tot);
55     b[tot+1]=0x7fffffff;
56     for(int i=1;i<=tot;i++) if(b[i]!=b[i+1]) b[++num]=b[i];
57     for(int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+1+num,a[i])-b;
58     for(int i=1;i<=n;i++) change(i,a[i],1);
59     for(int i=1;i<=m;i++){
60         if(q[i].c) printf("%d\n",b[get_ans(q[i].a-1,q[i].b,q[i].c)]);
61         else{
62             change(q[i].a,a[q[i].a],-1);
63             a[q[i].a]=lower_bound(b+1,b+1+num,q[i].b)-b;
64             change(q[i].a,a[q[i].a],1);
65         }
66     }
67     return 0;
68 }

前一种

 1 #include <cstdio>
 2 #include <algorithm>
 3 using namespace std;
 4
 5 const int maxn=10000+5;
 6 struct node{ int l,r,s; }t[maxn*400];
 7 struct qry{ int a,b,c; }q[maxn];
 8 int n,m,cnt,tot,num;
 9 int a[maxn],b[maxn<<1],root[maxn],L[maxn],R[maxn];
10
11 inline int lowbit(int x){ return x&(-x); }
12 void update(int l,int r,int &pos,int d,int s){
13     t[++cnt]=t[pos]; t[cnt].s+=s; pos=cnt;
14     if(l==r) return;
15     int mid=(l+r)/2;
16     if(d<=mid) update(l,mid,t[pos].l,d,s);
17     else update(mid+1,r,t[pos].r,d,s);
18 }
19 void change(int x,int d,int s){ for(;x<=n;x+=lowbit(x)) update(1,num,root[x],d,s); }
20 int query(int l,int r,int k,int cl,int cr){
21     if(l==r) return l;
22     int suml=0,sumr=0;
23     for(int i=1;i<=cl;i++) suml+=t[t[L[i]].l].s;
24     for(int i=1;i<=cr;i++) sumr+=t[t[R[i]].l].s;
25     int s=sumr-suml,mid=(l+r)/2;
26     if(k<=s){
27         for(int i=1;i<=cl;i++) L[i]=t[L[i]].l;
28         for(int i=1;i<=cr;i++) R[i]=t[R[i]].l;
29         return query(l,mid,k,cl,cr);
30     }
31     else{
32         for(int i=1;i<=cl;i++) L[i]=t[L[i]].r;
33         for(int i=1;i<=cr;i++) R[i]=t[R[i]].r;
34         return query(mid+1,r,k-s,cl,cr);
35     }
36 }
37 int get_ans(int l,int r,int k){
38     int cl,cr;
39     for(cl=0;l>0;l-=lowbit(l)) L[++cl]=root[l];
40     for(cr=0;r>0;r-=lowbit(r)) R[++cr]=root[r];
41     return query(1,num,k,cl,cr);
42 }
43 int main(){
44     scanf("%d%d",&n,&m);
45     char c;
46     for(int i=1;i<=n;i++) scanf("%d",&a[i]), b[++tot]=a[i];
47     for(int i=1;i<=m;i++){
48         for(c=getchar();c<‘A‘||c>‘Z‘;c=getchar());
49         scanf("%d%d",&q[i].a,&q[i].b);
50         if(c==‘Q‘) scanf("%d",&q[i].c);
51         else b[++tot]=q[i].b;
52     }
53     sort(b+1,b+1+tot);
54     b[tot+1]=0x7fffffff;
55     for(int i=1;i<=tot;i++) if(b[i]!=b[i+1]) b[++num]=b[i];
56     for(int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+1+num,a[i])-b;
57     for(int i=1;i<=n;i++) change(i,a[i],1);
58     for(int i=1;i<=m;i++){
59         if(q[i].c) printf("%d\n",b[get_ans(q[i].a-1,q[i].b,q[i].c)]);
60         else{
61             change(q[i].a,a[q[i].a],-1);
62             a[q[i].a]=lower_bound(b+1,b+1+num,q[i].b)-b;
63             change(q[i].a,a[q[i].a],1);
64         }
65     }
66     return 0;
67 }

后一种

另外这题可以用线段树套平衡树做(貌似树状数组套平衡树也是可以的?树状数组不太熟啊...)

  1 #include <cstdio>
  2 #include <cstdlib>
  3 #include <algorithm>
  4 using namespace std;
  5
  6 const int maxn=10000+5,oo=~0u>>1;
  7 int n,q;
  8 int a[maxn];
  9 char str[3];
 10 struct Treap{
 11     struct node{
 12         node* ch[2];
 13         int v,r,s,c;
 14         node(int v,node* t):v(v){ ch[0]=ch[1]=t; r=rand(); s=c=1; }
 15         void push_up(){ s=ch[0]->s+ch[1]->s+c; }
 16     }*root,*null;
 17     Treap(){
 18         null=new node(0,NULL);
 19         null->c=null->s=0; null->r=oo;
 20         root=null;
 21     }
 22     void rotate(node* &o,bool d){
 23         node* k=o->ch[!d]; o->ch[!d]=k->ch[d]; k->ch[d]=o;
 24         o->push_up(); k->push_up(); o=k;
 25     }
 26     void insert(node* &o,int x){
 27         if(o==null) o=new node(x,null);
 28         else{
 29             if(x==o->v) o->s++, o->c++;
 30             else{
 31                 bool d=x>o->v;
 32                 insert(o->ch[d],x);
 33                 if(o->ch[d]->r<o->r) rotate(o,!d);
 34                 o->push_up();
 35             }
 36         }
 37     }
 38     void remove(node* &o,int x){
 39         if(o->v==x){
 40             if(o->c>1) o->c--, o->s--;
 41             else{
 42                 if(o->ch[0]!=null&&o->ch[1]!=null){
 43                     bool d=o->ch[0]->r<o->ch[1]->r;
 44                     rotate(o,d); remove(o->ch[d],x); o->push_up();
 45                 }
 46                 else{
 47                     node* u=o;
 48                     o=o->ch[0]==null?o->ch[1]:o->ch[0];
 49                     delete u;
 50                 }
 51             }
 52         }
 53         else{
 54             bool d=x>o->v;
 55             remove(o->ch[d],x);
 56             o->push_up();
 57         }
 58     }
 59     int rank(int x){
 60         int ret=0;
 61         for(node* t=root;t!=null;){
 62             int s=t->ch[0]->s+t->c;
 63             if(x>t->v) ret+=s, t=t->ch[1];
 64             else t=t->ch[0];
 65         }
 66         return ret;
 67     }
 68     int pre(int x){
 69         int ret=-oo;
 70         for(node* t=root;t!=null;){
 71             if(t->v<x) ret=t->v, t=t->ch[1];
 72             else t=t->ch[0];
 73         }
 74         return ret;
 75     }
 76 };
 77 struct Segment_Tree{
 78     Treap tree[maxn*4];
 79     void build_tree(int l,int r,int k){
 80         for(int i=l;i<=r;i++) tree[k].insert(tree[k].root,a[i]);
 81         if(l==r) return;
 82         int mid=l+(r-l)/2;
 83         build_tree(l,mid,k<<1); build_tree(mid+1,r,k<<1|1);
 84     }
 85     int get_rank(int l,int r,int k,int x,int y,int X){
 86         if(l==x&&r==y) return tree[k].rank(X);
 87         int mid=l+(r-l)/2;
 88         if(y<=mid) return get_rank(l,mid,k<<1,x,y,X);
 89         else if(x>mid) return get_rank(mid+1,r,k<<1|1,x,y,X);
 90         else return get_rank(l,mid,k<<1,x,mid,X)+get_rank(mid+1,r,k<<1|1,mid+1,y,X);
 91     }
 92     void change(int l,int r,int k,int id,int x){
 93         tree[k].remove(tree[k].root,a[id]);
 94         tree[k].insert(tree[k].root,x);
 95         if(l==r) return;
 96         int mid=l+(r-l)/2;
 97         if(id<=mid) change(l,mid,k<<1,id,x);
 98         else change(mid+1,r,k<<1|1,id,x);
 99     }
100     int get_pre(int l,int r,int k,int x,int y,int X){
101         if(l==x&&r==y) return tree[k].pre(X);
102         int mid=l+(r-l)/2;
103         if(y<=mid) return get_pre(l,mid,k<<1,x,y,X);
104         else if(x>mid) return get_pre(mid+1,r,k<<1|1,x,y,X);
105         else return max(get_pre(l,mid,k<<1,x,mid,X),get_pre(mid+1,r,k<<1|1,mid+1,y,X));
106     }
107     int get_kth(int x,int y,int k){
108         long long l=-oo,r=oo;
109         while(l<r){
110             int mid=(int)(l+(r-l)/2);
111             int tmp=get_rank(1,n,1,x,y,mid)+1;
112             if(tmp<=k) l=mid+1;
113             else r=mid;
114         }
115         return get_pre(1,n,1,x,y,l);
116     }
117 }T;
118
119 int main(){
120     scanf("%d%d",&n,&q);
121     for(int i=1;i<=n;i++) scanf("%d",&a[i]);
122     T.build_tree(1,n,1);
123     for(int i=1;i<=q;i++){
124         scanf("%s",str);
125         int l,r,k,id,x;
126         if(str[0]==‘Q‘){
127             scanf("%d%d%d",&l,&r,&k);
128             printf("%d\n",T.get_kth(l,r,k));
129         }
130         else{
131             scanf("%d%d",&id,&x);
132             T.change(1,n,1,id,x);
133             a[id]=x;
134         }
135     }
136     return 0;
137 }

线段树+Treap

时间: 2024-10-07 16:06:32

BZOJ_1901_&_ZJU_2112_Dynamic_Rankings(主席树+树状数组/线段树+(Treap/Splay))的相关文章

hdu 1166 树状数组 线段树

敌兵布阵 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 51177    Accepted Submission(s): 21427 Problem Description C国的死对头A国这段时间正在进行军事演习,所以C国间谍头子Derek和他手下Tidy又开始忙乎了.A国在海岸线沿直线布置了N个工兵营地,Derek和Tidy的任务

hdu1394(枚举/树状数组/线段树单点更新&amp;区间求和)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1394 题意:给出一个循环数组,求其逆序对最少为多少: 思路:对于逆序对: 交换两个相邻数,逆序数 +1 或 -1, 交换两个不相邻数 a, b, 逆序数 += 两者间大于 a 的个数 - 两者间小于 a 的个数: 所以只要求出初始时的逆序对数,就可以推出其余情况时的逆序对数.对于求初始逆序对数,这里 n 只有 5e3,可以直接暴力 / 树状数组 / 线段树 / 归并排序: 代码: 1.直接暴力 1

HDU 1394 Minimum Inversion Number 树状数组&amp;&amp;线段树

题目给了你一串序列,然后每次 把最后一个数提到最前面来,直到原来的第一个数到了最后一个,每次操作都会产生一个新的序列,这个序列具有一个逆序数的值,问最小的你逆序数的值为多少 逆序数么 最好想到的是树状数组,敲了一把很快,注意把握把最后一个数提上来对逆序数的影响即可, #include<iostream> #include<cstdio> #include<list> #include<algorithm> #include<cstring> #i

HDU 1166 敌兵布阵 (树状数组&#183;线段树)

题意  中文 动态区间和问题   只会更新点  最基础的树状数组 线段树的应用 树状数组代码 #include <bits/stdc++.h> using namespace std; const int N = 50005; int c[N], n, m; void add(int p, int x) { while(p <= n) c[p] += x, p += p & -p; } int getSum(int p) { int ret = 0; while(p > 0

CCPC河南省赛B-树上逆序对| 离线处理|树状数组 + 线段树维护逆序对 + dfs序 + 离散化

B题地址:树上逆序对 有两个思路 方法一:线段树离线 + 树状数组或者线段树维护区间和 0:离散化,离线存储输入的operation操作序列. ①:先线段树在dfs序上离线处理好整一棵树:在dfs序上先查询"加入当前结点的逆序对权值和"并记录,再加入当前这个节点:dfs完毕后,就已经记录好每个结点的dfs序出入时间戳(转化成区间问题了)和每个 ②:使用树状数组或者新的线段树在dfs序上插入逆序对权值 为什么能这样呢?因为dfs序维护了每个结点遍历的顺序,每个结点的dfs序时间戳肯定比它

Curious Robin Hood(树状数组+线段树)

1112 - Curious Robin Hood    PDF (English) Statistics Forum Time Limit: 1 second(s) Memory Limit: 64 MB Robin Hood likes to loot rich people since he helps the poor people with this money. Instead of keeping all the money together he does another tri

士兵杀敌(四)(树状数组+线段树)

士兵杀敌(四) 时间限制:2000 ms  |  内存限制:65535 KB 难度:5 描述 南将军麾下有百万精兵,现已知共有M个士兵,编号为1~M,每次有任务的时候,总会有一批编号连在一起人请战(编号相近的人经常在一块,相互之间比较熟悉),最终他们获得的军功,也将会平分到每个人身上,这样,有时候,计算他们中的哪一个人到底有多少军功就是一个比较困难的事情,军师小工的任务就是在南将军询问他某个人的军功的时候,快速的报出此人的军功,请你编写一个程序来帮助小工吧. 假设起始时所有人的军功都是0. 输入

Color the ball(树状数组+线段树)

Color the ball Time Limit : 9000/3000ms (Java/Other)   Memory Limit : 32768/32768K (Java/Other) Total Submission(s) : 3   Accepted Submission(s) : 1 Problem Description N个气球排成一排,从左到右依次编号为1,2,3....N.每次给定2个整数a b(a <= b),lele便为骑上他的“小飞鸽"牌电动车从气球a开始到气球b

Codeforces Round #225 (Div. 1) C 树状数组 || 线段树

看到这题很开心啊,有印象跟以前做过的很像,貌似最近就做过一个,以时间戳为区间来建立树状数组,然后一开始我以为题意是,给x点加val,它以下的所有节点都加-val:所以一开始就以 加 和 减 建立了两个树状数组,最后 减去就是答案,写完发现跟案例对不上啊,读了题目也没发现读错了,对于那句话 我理解错了,后来看了 这个: http://blog.csdn.net/keshuai19940722/article/details/18967661 仔细看看处理部分,我还以为分奇偶性有规律呢,后来才发现读