您的位置:首页 > 其它

【BZOJ】【1901】【Zju2112】 Dynamic Rankings

2015-01-02 21:40 330 查看
再填个坑。

 动态维护区间第K大(带单点修改)

  首先裸的区间第K大我们是用的【前缀和】思想,实现O(n)预处理,O(1)找树查询,那么如果是动态的呢?我们可以利用树状数组(BIT)的思想,进行O(logn)的修改,O(logn)的查询(当然由于是在线段树上做,都各需要再乘logn的复杂度)

  也就是说,每次修改,一块改logn棵线段树;每次查询也是在logn棵线段树上一起往下找!

  P.S.本题需将所有数(包括修改后的数)进行离散化

//BZOJ 1901
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define rep(i,n) for(int i=0;i<n;++i)
#define F(i,j,n) for(int i=j;i<=n;++i)
#define D(i,j,n) for(int i=j;i>=n;--i)
#define lowbit(x) ((x)&(-(x)))
using namespace std;
const int N=110086;

struct Tree{
int cnt,l,r;
}t[N*30];
int n,m,num=0,root
,a[N>>1],b
,dat[N>>1][4],size=0,cnt=0;
int lc,rc,ln
,rn
;

#define mid (l+r>>1)
void build(int &o,int l,int r){
o=++cnt; t[o].cnt=0;
if (l==r) return;
build(t[o].l,l,mid);
build(t[o].r,mid+1,r);
}

void updata(int &o,int l,int r,int pos,int val){
t[++cnt]=t[o],o=cnt,t[o].cnt+=val;
if (l==r) return;
if (pos <= mid) updata(t[o].l,l,mid,pos,val);
else updata(t[o].r,mid+1,r,pos,val);
}

void modify(int x,int pos,int val){
for(x;x<=n;x+=lowbit(x) )//一次改logn棵树
updata(root[x],1,num,pos,val);
}

int query(int i,int j,int rank){
int l=1,r=num;
int tl=0,tr=0;

while(l!=r){
tl=tr=0;
F(i,1,lc) tl+=t[t[ln[i]].l].cnt;//将logn棵树的和加出来
F(i,1,rc) tr+=t[t[rn[i]].l].cnt;
if (tr-tl>=rank){
F(i,1,lc) ln[i]=t[ln[i]].l;//向左找
F(i,1,rc) rn[i]=t[rn[i]].l;
r=mid;
}
else{
F(i,1,lc) ln[i]=t[ln[i]].r;//向右找
F(i,1,rc) rn[i]=t[rn[i]].r;
l=mid+1; rank-=tr-tl;
}

}
return l;
}
#undef mid
int getans(int l,int r,int k){
rc=lc=0;
for(r;r;r-=lowbit(r))
rn[++rc]=root[r];
for(l;l;l-=lowbit(l))
ln[++lc]=root[l];
return query(1,num,k);
}

void solve(){
sort(b+1,b+size+1);
num=unique(b+1,b+size+1)-b-1;//这个神奇的用法……是什么意思?
F(i,1,n) a[i]=lower_bound(b+1,b+num+1,a[i])-b;
build(root[0],1,num);
F(i,1,n) modify(i,a[i],1);
F(i,1,m){
if(dat[i][0]==0) printf("%d\n",b[ getans(dat[i][1]-1,dat[i][2],dat[i][3]) ]);
else{
int pos=lower_bound(b+1,b+num+1,dat[i][2])-b;
modify(dat[i][1],a[dat[i][1]],-1);
a[dat[i][1]]=pos;
modify(dat[i][1],a[dat[i][1]],1);
}
}
}

int main(){
#ifndef ONLINE_JUDGE
freopen("file.in","r",stdin);
#endif
int T=1;
//    scanf("%d",&T);
while(T--){
size=cnt=num=0;
memset(b,0,sizeof b);
memset(t,0,sizeof t);
memset(dat,0,sizeof dat);
scanf("%d%d",&n,&m);
F(i,1,n){
scanf("%d",&a[i]);
b[++size]=a[i];
}
char cmd[3];
F(i,1,m){
scanf("%s",cmd);
if (cmd[0]=='C'){
dat[i][0]=1;
scanf("%d%d",&dat[i][1],&dat[i][2]);
b[++size]=dat[i][2];
}
else{
dat[i][0]=0;
scanf("%d%d%d",&dat[i][1],&dat[i][2],&dat[i][3]);
}
}
solve();
}
return 0;
}
/**************************************************
利用BIT的思想,实现前缀-差分的logn的转化
裸的可持久化线段树是每棵树维护一个区间[1,i](前缀和)
查询O(1),而修改就需要 O(n)了
而动态进行修改&查询-->BIT里套一个可持久化线段树
BIT的每个节点表示原数组的一个区间
然后用可持久化线段树来维护这个区间
查询的时候log(n)棵线段树一起查
**************************************************/


View Code
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: