POJ2104 区间第k大(带修改)

版权声明: https://blog.csdn.net/DancingZ/article/details/81748141

Description

  给定一个长度为N的已知序列A[i](1<=i<=N),要求维护这个序列,能够支持以下两种操作:
  1、查询A[i],A[i+1],A[i+2],…,A[j](1<=i<=j<=N)中,升序排列后排名第k的数。
  2、修改A[i]的值为j。
  所谓排名第k,指一些数按照升序排列后,第k位的数。例如序列{6,1,9,6,6},排名第3的数是6,排名第5的数是9。

Input

  输入文件的第一行包含两个整数N和M,分别表示序列的长度为N和有M个操作。
  接下来的N个不大于10^9正整数,第i个表示序列A[i]的初始值。
  然后的M行,每行为一个操作Q i j k 或者C i j分别表示查询A[i],A[i+1],A[i+2],…,A[j](1<=i<=j<=N)中,升序排列后排名第k的数和修改A[i]的值为j。

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。

打一发树套树,表示splay的哪个旋转到根下面的写法好迷。。

树套树常数贼大= =主席树貌似好用不少(还没复习233)

#include<bits/stdc++.h>
using namespace std;
#define Inc(i,L,r) for(register int i=(L);i<=(r);++i)
#define Red(i,r,L) for(register int i=(r);i>=(L);--i)
const int N = 1e4+10,Maxsiz = N*log2(N)*2;
int n,m,a[N];
struct Splay{
	int p[Maxsiz],ch[Maxsiz][2];
	int cnt,k[Maxsiz],siz[Maxsiz];
	#define Ls(v) ch[v][0]
	#define rs(v) ch[v][1]
	#define sum(v) siz[v]=siz[Ls(v)]+siz[rs(v)]+1
	inline void rotate(int x){
		int f=p[x],gf=p[f],tp=rs(f)==x,son=ch[x][!tp];
		ch[p[son]=f][tp]=son,sum(f);
		ch[p[f]=x][!tp]=f,sum(x);
		ch[p[x]=gf][rs(gf)==f]=x;
	}
	inline void splay(int x,int &rt){
		while(p[x]){
			if((p[p[x]])&&((rs(p[p[x]])==p[x])==(rs(p[x])==x)))rotate(p[x]);
			rotate(x);
		}
		rt=x;
	}
	inline void newnode(int x,int v,int Fa){
		siz[x]=1;
		p[x]=Fa;
		k[x]=v;
	}
	inline void insert(int &x,int v,int Fa,int &root){
		if(!x)return newnode(x=++cnt,v,Fa),splay(x,root),void();
		insert(ch[x][k[x]<v],v,x,root);
	}
	inline int Query(int x,int v){
		int tot=0;
		while(x)k[x]<=v?tot+=siz[Ls(x)]+1,x=rs(x):x=Ls(x);
		return tot;
	}
	inline int findpre(int x){
		for(x=Ls(x);rs(x);x=rs(x));
		return x;
	}
	inline int findsub(int x){
		for(x=rs(x);Ls(x);x=Ls(x));
		return x;
	}
	inline void delet(int x,int v,int &root){
		for(;k[x]^v;x=ch[x][k[x]<v]);
		splay(x,root);
		if(!Ls(x))return p[root=rs(x)]=0,void();
		int pre=findpre(x);
		splay(pre,root);
		rs(pre)=rs(x);
		if(rs(x))p[rs(x)]=pre;
		sum(pre);
	}
}sp;
struct SegMent{
	struct tree{
		int L,r,rt;
	}t[N<<2];
	#define Lc v<<1
	#define rc v<<1|1
	inline void Modify(int v,int x,bool cmd){
		if(t[v].L>x||t[v].r<x)return ;
		cmd?sp.insert(t[v].rt,a[x],0,t[v].rt):sp.delet(t[v].rt,a[x],t[v].rt);
		if(t[v].L==t[v].r)return ;
		Modify(Lc,x,cmd),Modify(rc,x,cmd);
	}
	inline int Query(int v,int A,int b,int k){
		if(t[v].L>b||t[v].r<A)return 0;
		if(A<=t[v].L&&t[v].r<=b)return sp.Query(t[v].rt,k);
		return Query(Lc,A,b,k)+Query(rc,A,b,k);
	}
	inline void build(int v,int L,int r){
		t[v]=(tree){L,r};
		Inc(i,L,r)sp.insert(t[v].rt,a[i],0,t[v].rt);
		if(L==r)return ;
		int Mid=L+r>>1;
		build(Lc,L,Mid),build(rc,Mid+1,r);
	}
}tr;
inline void init(){
	scanf("%d%d",&n,&m);
	Inc(i,1,n)scanf("%d",&a[i]);
	tr.build(1,1,n);
}
inline void Modify(){
	int x,k;scanf("%d%d",&x,&k);
	tr.Modify(1,x,0);
	a[x]=k;
	tr.Modify(1,x,1);
}
inline void Query(){
	int ans,L,r,kth;
	scanf("%d%d%d",&L,&r,&kth);
	for(int x=tr.t[1].rt;x;){
		int tmp=tr.Query(1,L,r,sp.k[x]);
		if(tmp<kth)x=sp.rs(x);
		else ans=sp.k[x],x=sp.Ls(x);
	}
	cout<<ans<<"\n";
}
inline void solv(){
	Inc(i,1,m){
		char c=getchar();while((c^'Q')&&(c^'C'))c=getchar();
		c=='Q'?Query():Modify();
	}
}
int main(){
	init();
	solv();
	return 0;
}

补一个主席树,,

和静态版本略有不同(我感觉完全不一样。。),说的可能有点乱,大概是那个意思。

查询区间[l,r],由于主席树的前缀和性质想到树状数组,常规的树状数组直接改点值,我们不过是把点值换成了整棵线段树。

简单地说,就是树状数组套上多棵动态开点的权值线段树。

但是这样写就不满足主席树只加点不删除的性质。。我也很迷~主席树的定义到底是个什么= =

再补:满足前缀和性质的线段树就是主席树~

#include<bits/stdc++.h>
using namespace std;
const int Maxn=10005;
int n,a[Maxn];
struct SegMent{
	struct tree{
		int ls,rs,siz;
	}t[Maxn*40];int cnt;
	inline void modify(int &x,int v,int l,int r,int cmd){
		if(l>v||r<v)return ;
		t[x?:x=++cnt].siz+=cmd;
		if(l==r)return ;
		int mid=l+r>>1;
		modify(t[x].ls,v,l,mid,cmd),modify(t[x].rs,v,mid+1,r,cmd);
	}
}seg;
struct BIT{
	int root[Maxn];
	int q[2][Maxn];
	#define lowbit(x) (x)&-(x)
	inline void update(int x,int k,int cmd){
		for(;x<=n;x+=lowbit(x))seg.modify(root[x],k,0,(1<<30),cmd);
	}
	inline void getroot(int l,int r){
		for(q[0][0]=0;l>0;l-=lowbit(l))q[0][++q[0][0]]=root[l];
		for(q[1][0]=0;r>0;r-=lowbit(r))q[1][++q[1][0]]=root[r];		
	}
	inline int sum(bool t){
		int ret=0;
		for(int i=1;i<=q[t][0];++i)ret+=seg.t[seg.t[q[t][i]].ls].siz;
		return ret;
	}
	inline int query(int l,int r,int k){
		if(l==r)return l;
		int size=sum(1)-sum(0);
		int mid=l+r>>1;
		if(size>=k){
			for(int i=1;i<=q[0][0];++i)q[0][i]=seg.t[q[0][i]].ls;
			for(int i=1;i<=q[1][0];++i)q[1][i]=seg.t[q[1][i]].ls;
			return query(l,mid,k);
		}else {
			for(int i=1;i<=q[0][0];++i)q[0][i]=seg.t[q[0][i]].rs;
			for(int i=1;i<=q[1][0];++i)q[1][i]=seg.t[q[1][i]].rs;
			return query(mid+1,r,k-size);
		}
	}
}bit;
int main(){
	int m;scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i)scanf("%d",&a[i]);
	for(int i=1;i<=n;++i)bit.update(i,a[i],1);
	for(int i=1;i<=m;++i){
		char c=getchar();while(!isalpha(c))c=getchar();
		if(c=='C'){
			int x,k;scanf("%d%d",&x,&k);
			bit.update(x,a[x],-1);
			bit.update(x,a[x]=k,1);
		}else {
			int l,r,k;scanf("%d%d%d",&l,&r,&k);
			bit.getroot(l-1,r);
			printf("%d\n",bit.query(0,(1<<30),k));
		}
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/DancingZ/article/details/81748141