[noip2017]列队 splay

noip出这个题简直丧心病狂,省选都没考过这么恶心的splay

这个题思路不是很难找,要点只有两个:特殊数据和动态结构

首先纵向发生变化的一定是最右边的一列,所以它是特殊的

然后询问只有1e5 ,而矩阵是1e5*1e5的,所以询问少,是特殊的

所有的操作等价于从一行中选择一个,插入到最后一列的队尾,从最后一列中选择一个,插入到一列的队尾

一开始想用离线线段树做,存sz,合并不会被询问的点团,但这样不被询问的点团也有可能被询问,所以还是需要动态维护

所以就需要动态的结构,用splay维护最右边一列、每一行

利用splay的sz和起始值大小来压缩区间,需要时分裂、删除和插入

非常难写,写了一天


码:

#include<iostream>
#include<cstdio>
using namespace std;
#define N 3000005
int ch[N][2],op,fu[N],rt[N],tot,q;
long long sz[N],szh[N],v[N],c,a,b,i,x,y,n,m;
void up(int o)
{
	szh[o]=szh[ch[o][0]]+szh[ch[o][1]]+sz[o];
}
void set(int a,int wh,int child)
{
	ch[a][wh]=child;
	fu[child]=a;
    up(a);
}
int getwh(int o)
{return ch[fu[o]][0]==o?0:1;
}
void rotate(int o)
{
	int fa=fu[o],ye=fu[fa];
	int wh=getwh(o);
	set(fa,wh,ch[o][wh^1]);
	set(o,wh^1,fa);
	fu[o]=ye;
	if(ye!=0)
  	{
    ch[ye][ch[ye][0]==fa?0:1]=o;		
	}	
}
void splay(int hang,int o,int tar)
{
	for(;fu[o]!=tar;rotate(o))
	if(fu[fu[o]]!=tar)	
	getwh(o)==getwh(fu[o])?rotate(fu[o]):rotate(o);
	if(tar==0)rt[hang]=o;
}
int xin()
{
v[++tot]=a;
sz[tot]=szh[tot]=b;
return tot;
}
void del(int hang,int o)
{
	splay(hang,o,0);
	if(ch[o][0]==0)
	{
		fu[ch[o][1]]=0;
		rt[hang]=ch[o][1];	
	}else if(ch[o][1]==0)
	{
		fu[ch[o][0]]=0;
		rt[hang]=ch[o][0];
	}else
	{
		int p=ch[o][0];
		while(ch[p][1])p=ch[p][1];
		ch[p][1]=ch[o][1];
		fu[ch[o][1]]=p;
		fu[ch[o][0]]=0;
	    rt[hang]=ch[o][0];splay(hang,p,0);
	}
}
void zhao(int qu,int bu,int mb)
{
	int o=rt[qu],last=0;
	while(o)
	{
		last=o;
		if(szh[ch[o][0]]+1<=mb&&szh[ch[o][0]]+sz[o]>=mb)
		{
			mb-=szh[ch[o][0]];
			c=v[o]+mb-1; 
			splay(qu,o,0); 
			if(op==0)//取出行里,补到列里 
			{
			if(sz[o]==1)//直接删 
			{
				del(qu,o);			
			}else			
			if(mb==1)//只裂出右边
			{
				a=c+1; b=sz[o]-1;
				int rr=xin();
				if(ch[o][1]==0)
				{
					set(o,1,rr);
				}else
				{
				int p=ch[o][1];
				while(ch[p][0])p=ch[p][0];
				set(p,0,rr);
			    }
			    splay(qu,rr,0);
			    del(qu,o);			    
			}
			else if(mb==sz[o])//只裂出左边 
			{
				a=v[o]; b=sz[o]-1;
				int ll=xin();
				if(ch[o][0]==0)
				{
					set(o,0,ll);
				}else
				{
				int p=ch[o][0];
				while(ch[p][1])p=ch[p][1];
				set(p,1,ll);
			    }
			    splay(qu,ll,0);
			    del(qu,o);		
			}else
			{
				a=c+1; b=sz[o]-mb;
				int rr=xin();
				if(ch[o][1]==0)
				{
					set(o,1,rr);
				}else
				{
				int p=ch[o][1];
				while(ch[p][0])p=ch[p][0];
				set(p,0,rr);
			    }
			 splay(qu,rr,0);
			splay(qu,o,0);
				a=v[o]; b=mb-1;
				int ll=xin();
				if(ch[o][0]==0)
				{
					set(o,0,ll);
				}else
				{
				int p=ch[o][0];
				while(ch[p][1])p=ch[p][1];
				set(p,1,ll);
			    }
			    splay(qu,ll,0);
			    del(qu,o);					
			}
			o=rt[bu];
			while(ch[o][1])o=ch[o][1];
			a=c,b=1;
			int rr=xin();			
			if(o)set(o,1,rr);
			else rt[bu]=rr;
			splay(bu,rr,0);
		    }else  if(op==1)//取出列里补到行里 
		    {
		    	c=v[o];
		    	del(qu,o);
			o=rt[bu];
			while(ch[o][1])o=ch[o][1];
			a=c,b=1;
			int rr=xin();			
			if(o)set(o,1,rr);else rt[bu]=rr;
			splay(bu,rr,0);  	
			}else//取出列里补到列里 
			{
			c=v[o];
		    del(qu,o);
			o=rt[bu];
			while(ch[o][1])o=ch[o][1];
			a=c,b=1;
			int rr=xin();			
			if(o)set(o,1,rr);else rt[bu]=rr;
			splay(bu,rr,0);  		
			}
		return;	
		}
		if(szh[ch[o][0]]>=mb)o=ch[o][0];
		else mb-=sz[o]+szh[ch[o][0]],o=ch[o][1];
	}
}
int jian(int l,int r)
{
	if(l>r)return 0;
	int mid=(l+r)>>1;
	sz[++tot]=1;
	szh[tot]=1;
	v[tot]=m*mid;
	int lin=tot;
	ch[lin][0]=jian(l,mid-1);
if(ch[lin][0])	fu[ch[lin][0]]=lin;	
	ch[lin][1]=jian(mid+1,r);
if(ch[lin][1])	fu[ch[lin][1]]=lin;	
	szh[lin]+=szh[ch[lin][0]]+szh[ch[lin][1]];
return lin;
}
int main()
{
	scanf("%lld%lld%d",&n,&m,&q);
	for(i=1;i<=n;i++)
	{
		a=(i-1)*m+1;
		b=m-1;
		rt[i]=xin();
	}
	rt[0]=tot+1;
	jian(1,n);
	for(i=1;i<=q;i++)
	{
		scanf("%d%d",&x,&y);
		if(y==m)
		{
			op=2;			
			zhao(0,0,x);
			printf("%lld\n",c);
		}else
		{
			op=0;
			zhao(x,0,y);	printf("%lld\n",c);
			op=1;
			zhao(0,x,x);
		}		
	}	
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值