树套树-区间第k大(带修改)

题目:zoj2112

题意:求区间第k大的数,有修改操作。

分析:这题可以树套树搞。

从网上看了很多博客才理解。。。资料1  资料2

我看的是树状数组套线段树版的。还有线段树套平衡树版的。


首先用一棵主席树(见这篇文章)维护没有操作前的数据。

然后用树状数组维护修改。

树状数组的每一个节点是一棵线段树,而且树状数组的每个节点都有一个管辖域(树状数组的性质没变)。

每更新一次,会修改log(n)个树状数组的节点。然而修改是不可取的,所以采用了新建了log(n)棵线段树的方法。

每新建一棵线段树,就是把原来版本的线段树先拷贝过来,然后新增一条路径(增加log(n)个节点)来代替那条要修改的路径。

代码(树状数组套线段树):

#include 
using namespace std;

typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 1E9+9;
const int maxn = 1e5+6;
const int LOG = 20;

struct node
{
	int l,r;
	int sum;
}Node[maxn*LOG];
int root[maxn],node_cnt;  //静态主席树,所用节点数 
int BIT[maxn];              //树状数组 
int numbers[maxn],num_cnt;   //离散化之后 
int a[maxn],n,q; 

struct command
{
	char op;
	int L,R,K;
}com[10006];

void build(int l,int r,int &rt)
{
	rt=++node_cnt;
	Node[rt].sum=0;
	if(l==r) return ;
	int m=l+r>>1;
	build(l,m,Node[rt].l);
	build(m+1,r,Node[rt].r);
}
void update(int pos,int v,int l,int r,int &rt,int pre)
{
	rt=++node_cnt;
	Node[rt]=Node[pre];
	Node[rt].sum+=v;
	if(l==r) return ;
	int m=(l+r)>>1;
	if(pos<=m)
		update(pos,v,l,m,Node[rt].l,Node[pre].l);
	else
		update(pos,v,m+1,r,Node[rt].r,Node[pre].r);
}

void modify(int pos,int v,int rt)
{
	for(int i=rt;i>1;
	if(k<=c)
		return query(k,l,m);
	else
		return query(k-c,m+1,r);
}

inline int Find(int x)
{
	return lower_bound(numbers+1,numbers+1+num_cnt,x)-numbers;
}
void Q(int L,int R,int K)
{
	buf_cnt1=0;
	buf1[buf_cnt1++]=root[L-1];
	for(int i=L-1;i>0;i-=(i&-i))
		buf1[buf_cnt1++]=BIT[i];
	
	buf_cnt2=0;
	buf2[buf_cnt2++]=root[R];
	for(int i=R;i>0;i-=(i&-i))
		buf2[buf_cnt2++]=BIT[i];
		
	int q=query(K,1,num_cnt);
	printf("%d\n",numbers[q]);
}
void C(int pos,int v)
{
	modify(Find(a[pos]),-1,pos);
	modify(Find(v),1,pos);
	a[pos]=v;
}
int main()
{
	int nCase;
	scanf("%d",&nCase);
	while(nCase--)
	{
		scanf("%d%d",&n,&q);
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&a[i]);
			numbers[i]=a[i];
		} 
		num_cnt=n;
		char ch[2];
		for(int i=1;i<=q;i++)
		{
			scanf("%s",ch);
			if(ch[0]=='Q')
			{
				scanf("%d%d%d",&com[i].L,&com[i].R,&com[i].K);
				com[i].op='Q';
			}
			else
			{
				scanf("%d%d",&com[i].L,&com[i].R);
				com[i].op='C';
				numbers[++num_cnt]=com[i].R; 
			}
		}
		
		sort(numbers+1,numbers+num_cnt+1);
		num_cnt=unique(numbers+1,numbers+num_cnt+1)-numbers-1;
		root[0]=node_cnt=0;
		build(1,num_cnt,root[0]);
		for(int i=1;i<=n;i++)
		{
			int f=Find(a[i]);
			update(f,1,1,num_cnt,root[i],root[i-1]);
		} 
		for(int i=1;i<=n;i++) //初始化树状数组,最初没有修改信息 
			BIT[i]=root[0];
		for(int i=1;i<=q;i++)
			'Q'==com[i].op?Q(com[i].L,com[i].R,com[i].K):C(com[i].L,com[i].R);
	}
	return 0;
}

线段树套reap会超内存,我是动态分配内存的,删除时也delete了。。。最多n*log(n)个node,也就是用了5*50000*16个int(32000*1000 bit),加上另外开的两个数组200000个int(1600*1000 bit),总共32813kb,刚刚超了。。。

求解方法。。。

#include 
using namespace std;

typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 1E9+9;

struct node          //定义Treap节点 
{
	node* son[2];     //左右子树 
	int v;   		  //值 
	int p;			  //优先级 
	int sz;           //以当前节点为根节点的树的大小 
	node(int x)
	{
		son[0]=son[1]=NULL;
		v=x;
		p=rand();
		sz=1;
	}
	void pushup()         //增加或删除节点需要修改子树的sz 
	{
		sz=1;
		if(son[0])	sz+=son[0]->sz;
		if(son[1])	sz+=son[1]->sz;
	}
};
inline int cmp(int a,int b)
{
	if(a==b)	return -1;
	return a>b?0:1;
}
inline void Rotate(node* &root,int d)    //d为0时左旋(右节点向上更新), d为1时右旋(左节点向上更新) 
{
	node* k = root->son[d^1];
	root->son[d^1] = k->son[d] ;
	k->son[d] = root;
	root->pushup();
	k->pushup();
	root = k ; 
}
void Insert(node* &root,int x)    
{
	if(NULL==root)
		root = new node(x);
	else
	{
		int d = root->v>x?0:1;        //这样写的话支持相同元素插入 
		Insert(root->son[d],x);
		if(root->pson[d]->p)
			Rotate(root,d^1);
	}
	if(root)
		root->pushup();
}
void Erase(node* &root,int x)      
{
	int d = cmp(root->v,x);
	if(-1==d)
	{
		node* u = root;
		if(!root->son[0])
		{
			root = root->son[1];
			delete u;
		}
		else if(!root->son[1])
		{
			root = root->son[0];
			delete u;
		}
		else
		{
			int d2 = (root->son[0]->p>root->son[1]->p?1:0);
			Rotate(root,d2);
			Erase(root->son[d2],x);
		}
	}
	else
		Erase(root->son[d],x);
	if(root)
		root->pushup();
}
void Destroy(node* &root)
{
	if(root->son[0])
		Destroy(root->son[0]);
	if(root->son[1])
		Destroy(root->son[1]);
	delete root;
	root=NULL;
}

int Find(node* root,int x)
{
	if(NULL==root)
		return 0;
	int ret=0,lnum=0;
	if(root->son[0])
		lnum+=root->son[0]->sz;
	if(x>=root->v)
		return lnum+1+Find(root->son[1],x);
	else
		return Find(root->son[0],x); 
}

#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxn = 50006;
node* tree[151000];
int a[maxn],n,m;

void update(int pos,int v,int d,int l,int r,int rt)
{
	d==1?Insert(tree[rt],v):Erase(tree[rt],v);
	if(l==r) return ;
	int m=(l+r)>>1;
	pos<=m?update(pos,v,d,lson):update(pos,v,d,rson);
}
int query(int L,int R,int x,int l,int r,int rt)
{
	if(L<=l && r<=R)
		return Find(tree[rt],x);
	int m=(l+r)>>1,ret(0);
	if(L<=m)
		ret+=query(L,R,x,lson);
	if(R>m)
		ret+=query(L,R,x,rson);
	return ret;
}

int work(node* root,int x)
{
	int ret=INF;
	while(root)
	{
		if(root->v>=x)
		{
			if(ret>root->v)
				ret=root->v;
			root=root->son[0];
		}
		else
			root=root->son[1];
	}
	return ret;
}

int q(int L,int R,int x,int l,int r,int rt)
{
	if(L<=l && r<=R)
		return work(tree[rt],x);
	int m=(l+r)>>1,ret=INF;
	if(L<=m)
		ret=min(ret,q(L,R,x,lson));
	if(R>m)
		ret=min(ret,q(L,R,x,rson));
	return ret;
}

void Q(int L,int R,int K)
{
	int down=-INF,up=INF,mid,ret;
	while(down>1;
	//	printf("%d\n",mid);
		int c=query(L,R,mid,1,n,1);
		if(c>=K)
			up=mid;
		else
			down=mid+1;
	}
	printf("%d\n",q(L,R,down,1,n,1));
}
void C(int pos,int v)
{
	update(pos,a[pos],0,1,n,1);
	update(pos,v,1,1,n,1);
	a[pos]=v;
}

int main()
{
	int nCase;
	scanf("%d",&nCase);
	while(nCase--)
	{
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++)
			scanf("%d",&a[i]);
		for(int i=0;i



你可能感兴趣的:(ACM-数据结构,ACM-模版,主席树,树套树)