【ZOJ2112】Dynamic Rankings-树状数组套主席树+离散化

测试地址:Dynamic Rankings

题目大意:维护一个长度为N的数列,支持以下操作:询问区间[i,j]内的第k小的元素,修改一个元素。

做法:这是一个很经典的问题:带单点修改的求区间第k小值问题。在我以前写的题解中我简述了用主席树求解不带修改的求区间第k小值问题的方法,那么今天这个带单点修改的问题要怎么办呢?我们知道在求解这类问题时,主席树中的各棵线段树实质上是一种前缀和,那么维护静态的区间和可以用前缀和维护,维护动态的区间和可以用树状数组维护,所以在这里就可以用树状数组套主席树来解决问题。在前面静态的问题中,第i棵线段树表示[1,i]这个区间内数的出现次数,利用将前缀和转化成树状数组的方法,转化之后的树状数组套主席树中的第i棵线段树表示[i-lowbit(i)+1,i]区间内数的出现次数,于是在修改某个元素时,运用类似树状数组维护动态前缀和的方法来维护与该元素相关的每棵线段树即可。询问区间[l,r]时将与元素A[l-1]和A[r]相关的各棵树的树根记录下来,然后再用类似静态问题中的二分查找方法查找即可。

可是这道题在ZOJ上的空间卡得很紧:32M,直接运用树状数组套主席树的方法的空间复杂度是O((N+M)log^2 N),会MLE,这时我们就用与处理静态问题时相同的一棵主席树维护原数列,再用一棵树状数组套主席树来维护在原数列基础上修改的值,这样空间复杂度降为O(NlogN+Mlog^2 N),可以用25M左右的空间AC。当然,原数列中元素的值可达10^9,需要将所有原数列中元素的值和修改操作中元素修改后的值离散化。这样问题就完美解决了。

以下是本人代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int T,n,m,a[50010],ops[10010][3],rt1[50010],rt2[50010],f,tot;
int L[50010],R[50010];
char op[10010][5];
struct forsort
{
  int val,pos;
}fs[100010];
struct segnode
{
  int lc,rc;
  int sum;
}seg[2000010];

int lowbit(int x)
{
  return x&(-x);
}

bool cmp(forsort a,forsort b)
{
  return a.val<b.val;
}

void trarr(int x,int *g)
{
  g[0]=0;
  for(;x>=1;x-=lowbit(x))
    g[++g[0]]=rt2[x];
}

void buildtree(int &no,int l,int r)
{
  no=++tot;
  seg[no].lc=seg[no].rc=seg[no].sum=0;
  if (l==r) return;
  int mid=(l+r)>>1;
  buildtree(seg[no].lc,l,mid);
  buildtree(seg[no].rc,mid+1,r);
}

void insert(int &no,int last,int l,int r,int k,int c)
{
  no=++tot;
  seg[no]=seg[last];
  if (l==r)
  {
    seg[no].sum+=c;
	return;
  }
  int mid=(l+r)>>1;
  if (k<=mid) insert(seg[no].lc,seg[last].lc,l,mid,k,c);
  else insert(seg[no].rc,seg[last].rc,mid+1,r,k,c);
  seg[no].sum=seg[seg[no].lc].sum+seg[seg[no].rc].sum;
}

int query(int fnt,int lst,int l,int r,int k)
{
  int s=seg[seg[lst].lc].sum-seg[seg[fnt].lc].sum,mid=(l+r)>>1;
  if (l==r) return l;
  for(int i=1;i<=R[0];i++)
    s+=seg[seg[R[i]].lc].sum;
  for(int i=1;i<=L[0];i++)
    s-=seg[seg[L[i]].lc].sum;
  if (s>=k)
  {
    for(int i=1;i<=R[0];i++)
	  R[i]=seg[R[i]].lc;
	for(int i=1;i<=L[0];i++)
	  L[i]=seg[L[i]].lc;
	return query(seg[fnt].lc,seg[lst].lc,l,mid,k);
  }
  else
  {
    for(int i=1;i<=R[0];i++)
	  R[i]=seg[R[i]].rc;
	for(int i=1;i<=L[0];i++)
	  L[i]=seg[L[i]].rc;
	return query(seg[fnt].rc,seg[lst].rc,mid+1,r,k-s);
  }
}

int main()
{
  scanf("%d",&T);
  while(T--)
  {
    tot=0;
    scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
	  scanf("%d",&fs[i].val);
	  fs[i].pos=i;
	}
	f=n;
	for(int i=1;i<=m;i++)
	{
	  scanf("%s",op[i]);
	  if (op[i][0]=='Q') scanf("%d%d%d",&ops[i][0],&ops[i][1],&ops[i][2]);
	  if (op[i][0]=='C')
	  {
	    scanf("%d%d",&ops[i][0],&fs[++f].val);
		fs[f].pos=n+i;
	  }
	}
	
	sort(fs+1,fs+f+1,cmp);
	for(int i=1;i<=f;i++)
	{
	  if (fs[i].pos<=n) a[fs[i].pos]=i;
	  else ops[fs[i].pos-n][1]=i;
	}
	
	buildtree(rt1[0],1,f);
	for(int i=1;i<=n;i++)
	  insert(rt1[i],rt1[i-1],1,f,a[i],1);
	buildtree(rt2[0],1,f);
	for(int i=1;i<=n;i++)
	  rt2[i]=rt2[0];
	
	for(int i=1,s,t,c;i<=m;i++)
	{
	  if (op[i][0]=='Q')
	  {
	    s=ops[i][0],t=ops[i][1],c=ops[i][2];
	    trarr(s-1,L);trarr(t,R);
		printf("%d\n",fs[query(rt1[s-1],rt1[t],1,f,c)].val);
	  }
	  else
	  {
	    s=ops[i][0],t=ops[i][1];
		int p=s;
		for(;s<=n;s+=lowbit(s))
		{
		  insert(rt2[s],rt2[s],1,f,a[p],-1);
		  insert(rt2[s],rt2[s],1,f,t,1);
	    }
		a[p]=t;
	  }
	}
  }
  
  return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值