NOIP2017 Day2 T3 列队

戳我看题面

考虑n=1的时候:

维护一个数列,每次删除一个数,再数列的结尾加上一个数,求第K大

这是splay,treap模板题

当然,不单单可以用他们来做

来可以用老少皆宜的线段树来做

维护01数列表示那个数是否存在,提前开出空间

删掉一个数标记成0,加上一个数常规加,求第K大就是线段树上二分

也可以用树状数组来做

与线段树操作一样,求第K大就是树状数组上倍增

 

当n!=1时

维护n+1个数列,每次操作如上

开n+1棵splay或者treap,或者动态开点线段树

不能开点,就意味着必须记删掉哪些数,注意开long long

 

当然,我只写了老少皆宜的线段树啦

#include<cstdio>
#include<iostream>
#define ll long long
using namespace std;

int read()
{
	int ret=0;
	char ch=getchar();
	while(ch<'0'||ch>'9')
		ch=getchar();
	while(ch>='0'&&ch<='9')
	{
		ret=(ret<<1)+(ret<<3)+ch-'0';
		ch=getchar(); 
	}
	return ret; 
}

void write(ll x)
{
	if(x/10) write(x/10);
	putchar(x%10+48); 
}

const int N=1e6+5,M=1e7+5;
int n,m,r,T,x,y,cnt,rt[N],lst[N],siz[M],ls[M],rs[M];
ll ans,c[M];

ll find(int &p,int l,int r,int x)
{
	if(!p) p=++cnt;
	if(l==r)
		return (c[p]==0)?-l:c[p]; 
	int mid=l+r>>1;
	if(mid-l+1-siz[ls[p]]>=x)
		return find(ls[p],l,mid,x);
	else 
		return find(rs[p],mid+1,r,x-(mid-l+1-siz[ls[p]]));
}

void del(int &p,int l,int r,int x)
{
	if(!p) p=++cnt;
	siz[p]++;
	if(l==r)
	{
		c[p]=-1;
		return;
	}
	int mid=l+r>>1;    
	if(mid-l+1-siz[ls[p]]>=x)
		del(ls[p],l,mid,x);
	else
		del(rs[p],mid+1,r,x-(mid-l+1-siz[ls[p]]));
}

void add(int &p,int l,int r,int x,ll y)
{
	if(!p) p=++cnt;
	if(l==r)
	{
		c[p]=y;
		return;
	}
	int mid=l+r>>1;
	if(x<=mid) 
		add(ls[p],l,mid,x,y);
	else 
		add(rs[p],mid+1,r,x,y); 
}

int main()
{
	//freopen("1.in","r",stdin);
	//freopen("1.out","w",stdout);
	n=read(),m=read(),T=read();
	r=max(n,m)+T;
	for(int i=1;i<=n;i++)
		lst[i]=m;
	lst[n+1]=n+1;
	while(T--)
	{
		x=read(),y=read();
		if(y!=m) 
		{
			ans=find(rt[x],1,r,y);
			if(ans<0) ans=((ll)x-1)*m-ans;
			del(rt[x],1,r,y);
			ll t=find(rt[n+1],1,r,x);
			if(t<0) t=-t*m;
			add(rt[x],1,r,lst[x],t);
			lst[x]++;
			del(rt[n+1],1,r,x);
			add(rt[n+1],1,r,lst[n+1],ans);
			lst[n+1]++;
		}else
		{
			ans=find(rt[n+1],1,r,x);
			if(ans<0) ans=-ans*m;
			del(rt[n+1],1,r,x);
			add(rt[n+1],1,r,lst[n+1],ans);
			lst[n+1]++;
		}
		write(ans);
		puts("");
	}
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值