BZOJ1901 Zju2112 Dynamic Rankings 主席树

欢迎访问~原文出处——博客园-zhouzhendong

去博客园看该题解


题目传送门 - BZOJ1901


题意概括

  给你一段序列(n个数),让你支持一些操作(共m次),

  有两种操作,一种是询问区间第k小,一种是单点修改。

  n,m<=10000


题解

  这个主席树的写法是我自己造出来的。

  主席树的查询区间第k大需要依赖前缀和。

  树状数组最擅长这个了,就让他来干。

  原理是这样的:

  先离散化,包括修改操作里面的数字也要离散化。

  然后建树,包括修改操作所涉及的数值也要建。

  现在总共有n个线段树。

  第0棵树是最完整的,故我们把这棵树的每一个节点都当作一个树状数组的起点,即该树状数组的第一项。

  然后往下一棵树走,当然下一棵树不一定有这个节点,那么不停的走下去,这个可以在建树的时候预先处理一个Next数组来优化。

  走到下一个这个位置的节点之后,我们再把这个节点作为该树状数组的第二项。

  这样我们有大约n*4(第0棵树的节点个数)个树状数组了。

  然而不处理要超空间。

  所以有两种方案,一种是vector,一种是把所有的树状数组连起来,变成一个大的。

  然而查询之类的就和普通的主席树差不多了。

细节上要大大的注意!


代码

#include <cstring>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <vector>
using namespace std;
const int N=10005,S=N*2*20*4;//v=4*node=4*20*hs=4*20*2*n
struct Que{
	char op;
	int a,b,c;
	void read(){
		char opc[5];
		scanf("%s%d%d",opc,&a,&b);
		op=opc[0];
		if (op=='Q')
			scanf("%d",&c);
	}
}q[N];
vector <int> vec[N];
int n,m,v[N],Ha[N*2],hs=0,total=0,totarr=0;
int root[N],ls[S],rs[S],sum[S],pos[S],Next[S],p[S];
void LSH(){
	int hs_=1;
	sort(Ha+1,Ha+hs+1);
	for (int i=2;i<=hs;i++)
		if (Ha[i]!=Ha[i-1])
			Ha[++hs_]=Ha[i];
	hs=hs_;
}
int find(int x){
	int L=1,R=hs,mid;
	while (L<=R){
		mid=(L+R)>>1;
		if (Ha[mid]==x)
			return mid;
		if (Ha[mid]<x)
			L=mid+1;
		else
			R=mid-1;
	}
	exit(233);
}
int build(int L,int R){
	int rt=++total;
	if (L==R){
		ls[rt]=rs[rt]=0;
		return rt;
	}
	int mid=(L+R)>>1;
	ls[rt]=build(L,mid);
	rs[rt]=build(mid+1,R);
	return rt;
}
int lowbit(int x){
	return x&-x;
}
void add(int x,int v){
	for (;x<=totarr;x+=lowbit(x))
		sum[x]+=v;
}
int Sum(int x){
	int ans=0;
	for (;x>0;x-=lowbit(x))
		ans+=sum[x];
	return ans;
}
void add_tree(int prt,int &rt,int L,int R,int pos){
	if (!rt||rt==prt)
		rt=++total;
	Next[prt]=rt;
	if (L==R)
		return;
	int mid=(L+R)>>1;
	if (pos<=mid){
		add_tree(ls[prt],ls[rt],L,mid,pos);
		if (!rs[rt])
			rs[rt]=rs[prt];
	}
	else {
		add_tree(rs[prt],rs[rt],mid+1,R,pos);
		if (!ls[rt])
			ls[rt]=ls[prt];
	}
}
void build_treearr(int srt,int L,int R){
	for (int i=srt;i;i=Next[i])
		p[i]=++totarr;
	if (L==R)
		return;
	int mid=(L+R)>>1;
	build_treearr(ls[srt],L,mid);
	build_treearr(rs[srt],mid+1,R);
}
void update(int rt,int L,int R,int pos,int v){
	add(p[rt],v);
	if (L==R)
		return;
	int mid=(L+R)>>1;
	if (pos<=mid)
		update(ls[rt],L,mid,pos,v);
	else
		update(rs[rt],mid+1,R,pos,v);
}
int query(int prt,int rt,int L,int R,int k){
	if (L==R)
		return Ha[L];
	int mid=(L+R)>>1;
	int LV=Sum(p[ls[rt]])-Sum(p[ls[prt]]);
	if (k<=LV)
		return query(ls[prt],ls[rt],L,mid,k);
	else
		return query(rs[prt],rs[rt],mid+1,R,k-LV);
}
int main(){
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++)
		scanf("%d",&v[i]),Ha[++hs]=v[i];
	for (int i=1;i<=m;i++){
		q[i].read();
		if (q[i].op=='C')
			Ha[++hs]=q[i].b;
	}
	LSH();
	for (int i=1;i<=n;i++)
		vec[i].clear();
	for (int i=1;i<=n;i++)
		vec[i].push_back(v[i]);
	for (int i=1;i<=m;i++)
		if (q[i].op=='C')
			vec[q[i].a].push_back(q[i].b);
	memset(sum,0,sizeof sum);
	memset(pos,0,sizeof pos);
	memset(Next,0,sizeof Next);
	memset(root,0,sizeof root);
	root[0]=build(1,hs);
	for (int i=1;i<=n;i++)
		for (int j=0;j<vec[i].size();j++)
			add_tree(root[i-1],root[i],1,hs,find(vec[i][j]));
	build_treearr(root[0],1,hs);
	for (int i=1;i<=n;i++)
		update(root[i],1,hs,find(v[i]),1);
	for (int i=1;i<=m;i++)
		if (q[i].op=='C'){
			update(root[q[i].a],1,hs,find(v[q[i].a]),-1);
			update(root[q[i].a],1,hs,find(v[q[i].a]=q[i].b),1);
		}
		else
			printf("%d\n",query(root[q[i].a-1],root[q[i].b],1,hs,q[i].c));
	return 0;
} 

  

posted @ 2017-12-14 15:08  zzd233  阅读(301)  评论(0编辑  收藏  举报