描述


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

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

1901: Zju2112 Dynamic Rankings

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 6716  Solved: 2793
[Submit][Status][Discuss]

Description


定一个含有n个数的序列a[1],a[2],a[3]……a[n],程序必须回答这样的询问:对于给定的i,j,k,在
a[i],a[i+1],a[i+2]……a[j]中第k小的数是多少(1≤k≤j-i+1),并且,你可以改变一些a[i]的值,改变后,程序还能针对
改变后的a继续回答上面的问题。你需要编一个这样的程序,从输入文件中读入序列a,然后读入一系列的指令,包括询问指令和修改指令。对于每一个询问指令,
你必须输出正确的回答。
第一行有两个正整数n(1≤n≤10000),m(1≤m≤10000)。分别表示序列的长度和指令的个数。第二行有n个数,表示
a[1],a[2]……a[n],这些数都小于10^9。接下来的m行描述每条指令,每行的格式是下面两种格式中的一种。 Q i j k 或者 C i
t Q i j k (i,j,k是数字,1≤i≤j≤n,
1≤k≤j-i+1)表示询问指令,询问a[i],a[i+1]……a[j]中第k小的数。C i t
(1≤i≤n,0≤t≤10^9)表示把a[i]改变成为t。

Input

对于每一次询问,你都需要输出他的答案,每一个输出占单独的一行。

Output

Sample Input

5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3

Sample Output

3
6

HINT

20%的数据中,m,n≤100; 40%的数据中,m,n≤1000; 100%的数据中,m,n≤10000。

Source

分析


对于只有第一种询问的问题: 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),但实际上远远用不到),虽然也可以写成每次修改无论之前有没有都直接复制一份,但是在空间不确定的情况下,写成前一种不容易爆炸.

前一种:

 #include <cstdio>
#include <algorithm>
using namespace std; const int maxn=+;
struct node{ int l,r,s; }t[maxn*];
struct qry{ int a,b,c; }q[maxn];
int n,m,cnt,tot,num;
int a[maxn],b[maxn<<],root[maxn],L[maxn],R[maxn]; inline int lowbit(int x){ return x&(-x); }
void update(int l,int r,int &pos,int d,int s){
if(!pos)t[++cnt]=t[pos], pos=cnt;
t[pos].s+=s;
if(l==r) return;
int mid=(l+r)/;
if(d<=mid) update(l,mid,t[pos].l,d,s);
else update(mid+,r,t[pos].r,d,s);
}
void change(int x,int d,int s){ for(;x<=n;x+=lowbit(x)) update(,num,root[x],d,s); }
int query(int l,int r,int k,int cl,int cr){
if(l==r) return l;
int suml=,sumr=;
for(int i=;i<=cl;i++) suml+=t[t[L[i]].l].s;
for(int i=;i<=cr;i++) sumr+=t[t[R[i]].l].s;
int s=sumr-suml,mid=(l+r)/;
if(k<=s){
for(int i=;i<=cl;i++) L[i]=t[L[i]].l;
for(int i=;i<=cr;i++) R[i]=t[R[i]].l;
return query(l,mid,k,cl,cr);
}
else{
for(int i=;i<=cl;i++) L[i]=t[L[i]].r;
for(int i=;i<=cr;i++) R[i]=t[R[i]].r;
return query(mid+,r,k-s,cl,cr);
}
}
int get_ans(int l,int r,int k){
int cl,cr;
for(cl=;l>;l-=lowbit(l)) L[++cl]=root[l];
for(cr=;r>;r-=lowbit(r)) R[++cr]=root[r];
return query(,num,k,cl,cr);
}
int main(){
scanf("%d%d",&n,&m);
char c;
for(int i=;i<=n;i++) scanf("%d",&a[i]), b[++tot]=a[i];
for(int i=;i<=m;i++){
for(c=getchar();c<'A'||c>'Z';c=getchar());
scanf("%d%d",&q[i].a,&q[i].b);
if(c=='Q') scanf("%d",&q[i].c);
else b[++tot]=q[i].b;
}
sort(b+,b++tot);
b[tot+]=0x7fffffff;
for(int i=;i<=tot;i++) if(b[i]!=b[i+]) b[++num]=b[i];
for(int i=;i<=n;i++) a[i]=lower_bound(b+,b++num,a[i])-b;
for(int i=;i<=n;i++) change(i,a[i],);
for(int i=;i<=m;i++){
if(q[i].c) printf("%d\n",b[get_ans(q[i].a-,q[i].b,q[i].c)]);
else{
change(q[i].a,a[q[i].a],-);
a[q[i].a]=lower_bound(b+,b++num,q[i].b)-b;
change(q[i].a,a[q[i].a],);
}
}
return ;
} 前一种

后一种:

 #include <cstdio>
#include <algorithm>
using namespace std; const int maxn=+;
struct node{ int l,r,s; }t[maxn*];
struct qry{ int a,b,c; }q[maxn];
int n,m,cnt,tot,num;
int a[maxn],b[maxn<<],root[maxn],L[maxn],R[maxn]; inline int lowbit(int x){ return x&(-x); }
void update(int l,int r,int &pos,int d,int s){
t[++cnt]=t[pos]; t[cnt].s+=s; pos=cnt;
if(l==r) return;
int mid=(l+r)/;
if(d<=mid) update(l,mid,t[pos].l,d,s);
else update(mid+,r,t[pos].r,d,s);
}
void change(int x,int d,int s){ for(;x<=n;x+=lowbit(x)) update(,num,root[x],d,s); }
int query(int l,int r,int k,int cl,int cr){
if(l==r) return l;
int suml=,sumr=;
for(int i=;i<=cl;i++) suml+=t[t[L[i]].l].s;
for(int i=;i<=cr;i++) sumr+=t[t[R[i]].l].s;
int s=sumr-suml,mid=(l+r)/;
if(k<=s){
for(int i=;i<=cl;i++) L[i]=t[L[i]].l;
for(int i=;i<=cr;i++) R[i]=t[R[i]].l;
return query(l,mid,k,cl,cr);
}
else{
for(int i=;i<=cl;i++) L[i]=t[L[i]].r;
for(int i=;i<=cr;i++) R[i]=t[R[i]].r;
return query(mid+,r,k-s,cl,cr);
}
}
int get_ans(int l,int r,int k){
int cl,cr;
for(cl=;l>;l-=lowbit(l)) L[++cl]=root[l];
for(cr=;r>;r-=lowbit(r)) R[++cr]=root[r];
return query(,num,k,cl,cr);
}
int main(){
scanf("%d%d",&n,&m);
char c;
for(int i=;i<=n;i++) scanf("%d",&a[i]), b[++tot]=a[i];
for(int i=;i<=m;i++){
for(c=getchar();c<'A'||c>'Z';c=getchar());
scanf("%d%d",&q[i].a,&q[i].b);
if(c=='Q') scanf("%d",&q[i].c);
else b[++tot]=q[i].b;
}
sort(b+,b++tot);
b[tot+]=0x7fffffff;
for(int i=;i<=tot;i++) if(b[i]!=b[i+]) b[++num]=b[i];
for(int i=;i<=n;i++) a[i]=lower_bound(b+,b++num,a[i])-b;
for(int i=;i<=n;i++) change(i,a[i],);
for(int i=;i<=m;i++){
if(q[i].c) printf("%d\n",b[get_ans(q[i].a-,q[i].b,q[i].c)]);
else{
change(q[i].a,a[q[i].a],-);
a[q[i].a]=lower_bound(b+,b++num,q[i].b)-b;
change(q[i].a,a[q[i].a],);
}
}
return ;
}

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

线段树+Treap:

 #include <cstdio>
#include <cstdlib>
#include <algorithm>
using namespace std; const int maxn=+,oo=~0u>>;
int n,q;
int a[maxn];
char str[];
struct Treap{
struct node{
node* ch[];
int v,r,s,c;
node(int v,node* t):v(v){ ch[]=ch[]=t; r=rand(); s=c=; }
void push_up(){ s=ch[]->s+ch[]->s+c; }
}*root,*null;
Treap(){
null=new node(,NULL);
null->c=null->s=; null->r=oo;
root=null;
}
void rotate(node* &o,bool d){
node* k=o->ch[!d]; o->ch[!d]=k->ch[d]; k->ch[d]=o;
o->push_up(); k->push_up(); o=k;
}
void insert(node* &o,int x){
if(o==null) o=new node(x,null);
else{
if(x==o->v) o->s++, o->c++;
else{
bool d=x>o->v;
insert(o->ch[d],x);
if(o->ch[d]->r<o->r) rotate(o,!d);
o->push_up();
}
}
}
void remove(node* &o,int x){
if(o->v==x){
if(o->c>) o->c--, o->s--;
else{
if(o->ch[]!=null&&o->ch[]!=null){
bool d=o->ch[]->r<o->ch[]->r;
rotate(o,d); remove(o->ch[d],x); o->push_up();
}
else{
node* u=o;
o=o->ch[]==null?o->ch[]:o->ch[];
delete u;
}
}
}
else{
bool d=x>o->v;
remove(o->ch[d],x);
o->push_up();
}
}
int rank(int x){
int ret=;
for(node* t=root;t!=null;){
int s=t->ch[]->s+t->c;
if(x>t->v) ret+=s, t=t->ch[];
else t=t->ch[];
}
return ret;
}
int pre(int x){
int ret=-oo;
for(node* t=root;t!=null;){
if(t->v<x) ret=t->v, t=t->ch[];
else t=t->ch[];
}
return ret;
}
};
struct Segment_Tree{
Treap tree[maxn*];
void build_tree(int l,int r,int k){
for(int i=l;i<=r;i++) tree[k].insert(tree[k].root,a[i]);
if(l==r) return;
int mid=l+(r-l)/;
build_tree(l,mid,k<<); build_tree(mid+,r,k<<|);
}
int get_rank(int l,int r,int k,int x,int y,int X){
if(l==x&&r==y) return tree[k].rank(X);
int mid=l+(r-l)/;
if(y<=mid) return get_rank(l,mid,k<<,x,y,X);
else if(x>mid) return get_rank(mid+,r,k<<|,x,y,X);
else return get_rank(l,mid,k<<,x,mid,X)+get_rank(mid+,r,k<<|,mid+,y,X);
}
void change(int l,int r,int k,int id,int x){
tree[k].remove(tree[k].root,a[id]);
tree[k].insert(tree[k].root,x);
if(l==r) return;
int mid=l+(r-l)/;
if(id<=mid) change(l,mid,k<<,id,x);
else change(mid+,r,k<<|,id,x);
}
int get_pre(int l,int r,int k,int x,int y,int X){
if(l==x&&r==y) return tree[k].pre(X);
int mid=l+(r-l)/;
if(y<=mid) return get_pre(l,mid,k<<,x,y,X);
else if(x>mid) return get_pre(mid+,r,k<<|,x,y,X);
else return max(get_pre(l,mid,k<<,x,mid,X),get_pre(mid+,r,k<<|,mid+,y,X));
}
int get_kth(int x,int y,int k){
long long l=-oo,r=oo;
while(l<r){
int mid=(int)(l+(r-l)/);
int tmp=get_rank(,n,,x,y,mid)+;
if(tmp<=k) l=mid+;
else r=mid;
}
return get_pre(,n,,x,y,l);
}
}T; int main(){
scanf("%d%d",&n,&q);
for(int i=;i<=n;i++) scanf("%d",&a[i]);
T.build_tree(,n,);
for(int i=;i<=q;i++){
scanf("%s",str);
int l,r,k,id,x;
if(str[]=='Q'){
scanf("%d%d%d",&l,&r,&k);
printf("%d\n",T.get_kth(l,r,k));
}
else{
scanf("%d%d",&id,&x);
T.change(,n,,id,x);
a[id]=x;
}
}
return ;
}

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

  1. 洛谷P2414 阿狸的打字机 [NOI2011] AC自动机+树状数组/线段树

    正解:AC自动机+树状数组/线段树 解题报告: 传送门! 这道题,首先想到暴力思路还是不难的,首先看到y有那么多个,菜鸡如我还不怎么会可持久化之类的,那就直接排个序什么的然后按顺序做就好,这样听说有7 ...

  2. 树状数组 && 线段树应用 -- 求逆序数

    参考:算法学习(二)——树状数组求逆序数 .线段树或树状数组求逆序数(附例题) 应用树状数组 || 线段树求逆序数是一种很巧妙的技巧,这个技巧的关键在于如何把原来单纯的求区间和操作转换为 求小于等于a ...

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

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1394 题意:给出一个循环数组,求其逆序对最少为多少: 思路:对于逆序对: 交换两个相邻数,逆序数 +1 ...

  4. hdu 5147 Sequence II【树状数组/线段树】

    Sequence IITime Limit: 5000/2500 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Problem ...

  5. hdu 1166:敌兵布阵(树状数组 / 线段树,入门练习题)

    敌兵布阵 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submis ...

  6. 数据结构--树状数组&&线段树--基本操作

    随笔目的:方便以后对树状数组(BIT)以及基本线段树的回顾 例题链接:http://acm.hdu.edu.cn/showproblem.php?pid=1166 例题:hdu 1166 敌兵布阵 T ...

  7. BZOJ 3333 排队计划 树状数组+线段树

    题目大意:给定一个序列.每次选择一个位置,把这个位置之后全部小于等于这个数的数抽出来,排序,再插回去,求每次操作后的逆序对数 首先我们每一次操作 对于这个位置前面的数 因为排序的数与前面的数位置关系不 ...

  8. 第十四个目标(dp + 树状数组 + 线段树)

    Problem 2236 第十四个目标 Accept: 17    Submit: 35 Time Limit: 1000 mSec    Memory Limit : 32768 KB  Probl ...

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

    1112 - Curious Robin Hood    PDF (English) Statistics Forum Time Limit: 1 second(s) Memory Limit: 64 ...

随机推荐

  1. JavaScript之String()和.toString()

    JS中 转换字符串的方法有两个 一个String(),一个.toString(). 通常情况下 这两种使用没有太大的区别.但是需要注意几点: undefined: toString() var tes ...

  2. Eclipse 和 Intellij idea 快捷键的区别

    描述 Eclipse IntelliJ 代码补全 Ctrl+space ctrl+space 打开类或者接口 (两个IDE都支持使用“驼峰字符”前缀的方式来过滤查找列表,进而轻松完成搜索:比如:可以使 ...

  3. [002] The Perks of Being a Wallflower - 读后记

    The Perks of Being a Wallflower 今天(2015年10月30日 18:26:17)读完"The Perks of Being a Wallflower" ...

  4. Java基础巩固----泛型

    注:参考书籍:Java语言程序设计.本篇文章为读书笔记,供大家参考学习使用 1.使用泛型的主要优点是能够在编译时而不是在运行时检查出错误,提高了代码的安全性和可读性,同时也提高了代码的复用性. 1.1 ...

  5. Android学习5—布局简介

    Android界面的布局主要有四种,分别为RelativeLayout.LinearLayout.TableLayout.FrameLayout,接下来分别介绍这些布局如何使用(为了简单起见,接下来的 ...

  6. Mock相关收集

    MockMVC+Mockito http://www.cnblogs.com/syxchina/p/4150879.html Spring中使用Mockito http://www.cnblogs.c ...

  7. thinkphp分页格式的完全自定义,直接输入数字go到输入数字页

    实现分页效果如下: 以下标注红色字体的为重点   找到文件page.class.php在ThinkPHP/Library/Thinkpage.class.php并打开文件,复制函数show,在本文件中 ...

  8. PHP学习笔记(2) - 对PHP的印象

    一.PHP是一种简单易学的面向过程的弱类型动态脚本语言,本为制作简单的个人网站而开发,现如今经过多个版本的衍变甚至加入了一些面向对象的特性.PHP试图通过发展打进企业级开发,同时也使得它自身也越来越复 ...

  9. 翻译:ECMAScript 5.1简介

    简介 ECMAScript 5.1 (或仅 ES5) 是ECMAScript(基于JavaScript的规范)标准最新修正. 与HTML5规范进程本质类似,ES5通过对现有JavaScript方法添加 ...

  10. windows下Apache配置SSL安全连接

    什么是SSL? SSL(Secure Socket Layer): 是为Http传输提供安全的协议,通过证书认证来确保客户端和网站服务器之间的数据是安全.Open SSL下载地址:http://www ...