POJ2104 区间第k大(带修改)

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
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]x||t[v].rb||t[v].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

补一个主席树,,

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

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

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

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

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

#include
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>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;
}

 

你可能感兴趣的:(数据结构,splay,线段树,树状数组,主席树)