ZOJ 2112

我擦、、真难,看了mj的代码,才会写、、WA了无数,终于AC,删除写错几次,线段树写错几次,果然数据结构不多写就悲剧。。。

做法是线段树里面套个SBT,这样替换操作,相当于每个包含这点的线段的Sbt执行一次insert和del,然后对于询问,二分答案,看>于它的是否超过k个。取下界即可。

#include<cstdio>
#include<cstring>
int T,n,m,a,b,c,tot,p[50010];
char str[2];
struct sbt{int l,r,s,w;}tr[1000010];
struct segtree{int l,r,root;}seg[1000010];
void left(int &p)
{
	int t=tr[p].r;
	tr[p].r=tr[t].l;
	tr[t].l=p;
	tr[p].s=tr[tr[p].l].s+tr[tr[p].r].s+1;
	p=t;
	tr[p].s=tr[tr[p].l].s+tr[tr[p].r].s+1;
}
void right(int &p)
{
	int t=tr[p].l;
	tr[p].l=tr[t].r;
	tr[t].r=p;
	tr[p].s=tr[tr[p].l].s+tr[tr[p].r].s+1;
	p=t;
	tr[p].s=tr[tr[p].l].s+tr[tr[p].r].s+1;
}
void maintain(int &p,int flag)
{
	if(flag==0)
	{
		if(tr[tr[tr[p].l].l].s>tr[tr[p].r].s)
			right(p);
		else
			if(tr[tr[tr[p].l].r].s>tr[tr[p].r].s)
			{
				left(tr[p].l);
				right(p);
			}
			else
				return;
	}
	else
	{	
		if(tr[tr[tr[p].r].r].s>tr[tr[p].l].s)
			left(p);
		else
			if(tr[tr[tr[p].r].l].s>tr[tr[p].l].s)
			{
				right(tr[p].r);
				left(p);
			}
			else
				return;
	}
	maintain(tr[p].l,0);
	maintain(tr[p].r,1);
	maintain(p,0);
	maintain(p,1);
}
void ins(int &p,int x)
{
	if(p==0)
	{
		tr[++tot].l=0;
		tr[tot].r=0;
		tr[tot].w=x;
		tr[tot].s=1;
		p=tot;
		return;
	}
	if(x<=tr[p].w)ins(tr[p].l,x);
	else ins(tr[p].r,x);
	tr[p].s=tr[tr[p].l].s+tr[tr[p].r].s+1;
	maintain(p,x>=tr[p].w);
}
int del(int &p,int w)
{
	tr[p].s--;
	if((tr[p].w==w)||(tr[p].l==0&&w<tr[p].w)||(tr[p].r==0&&w>tr[p].w))
	{
		int nu=tr[p].w;
		if(tr[p].l==0||tr[p].r==0)
		p=tr[p].l+tr[p].r;
		else
		tr[p].w=del(tr[p].l,1000000003);
		return nu;
	}
	else
		if(tr[p].w<w)return del(tr[p].r,w);
		else
			return del(tr[p].l,w);
}
void build(int l,int r,int x)
{
	seg[x].l=l,seg[x].r=r;
	seg[x].root=0;
	for(int i=l;i<=r;i++)
		ins(seg[x].root,p[i]);
	if(l==r)return;
	int m=(l+r)>>1;
	build(l,m,2*x);
	build(m+1,r,2*x+1);
}
void change(int pos,int x,int y,int i)
{
	if(pos<seg[i].l||pos>seg[i].r)return;
	del(seg[i].root,x);
	ins(seg[i].root,y);
	if(seg[i].l<seg[i].r)
	{
	change(pos,x,y,2*i);
	change(pos,x,y,2*i+1);
	}
}
int rank(int &p,int x)
{
	if(p==0)return 0;
	if(tr[p].w>x)return rank(tr[p].l,x);
	else
		return (tr[tr[p].l].s+1+rank(tr[p].r,x));
}
int gao(int l,int r,int x,int i)
{
	if(l>seg[i].r||r<seg[i].l)return 0;
	if(l<=seg[i].l&&r>=seg[i].r)return rank(seg[i].root,x);
	return gao(l,r,x,2*i)+gao(l,r,x,2*i+1);
}
int main()
{
	scanf("%d",&T);
	while(T--)
	{
		tot=0;
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++)
			scanf("%d",p+i);
		build(1,n,1);
		for(int i=0;i<m;i++)
		{
			scanf("%s",str);
			if(*str=='C')
			{
				scanf("%d%d",&a,&b);
				change(a,p[a],b,1);
				p[a]=b;
			}
			else
			{
				scanf("%d%d%d",&a,&b,&c);
				int l=0,r=1000000000;
				while(l<r)
				{
					int m=(l+r)>>1;
					if(gao(a,b,m,1)>=c)
						r=m;
					else
						l=m+1;
				}
				printf("%d\n",r);
			}
		}
	}
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值