码迷,mamicode.com
首页 > 其他好文 > 详细

[NOIP2017]列队

时间:2018-04-22 17:22:53      阅读:189      评论:0      收藏:0      [点我收藏+]

标签:log   printf   upd   bit   vijos   初始   void   空间复杂度   span   

题目:洛谷P3960、Vijos P2033。

题目大意:

有一个$n\times m$的方阵,第$i$行第$j$列的人的编号是$(i-1)\times m+j$。

现在有$q$个出列操作,每次让一个人出列,然后让这个人所在行向左看齐,再让最后一列向前看齐,最后让这个人站到第$n$行第$m$列的位置。

你需要输出每次出列的人的编号。

解题思路:

最容易想到的,就是每行维护一棵平衡树,再给最后一列维护一棵平衡树(正解是用树状数组)。

但是空间不够啊!

我们发现,每行的人初始编号是连续的,而对于$9\times 10 ^{10}$的人数,$3\times 10 ^5$次询问改变的人数其实并不多。

所以,我们把同一行内编号连续的一段缩成一个点,然后需要出列时再分裂即可。

然后用Treap一顿split和merge就可以了。

时间复杂度$O(q\log n)$。

空间复杂度$O($玄学$)$。

在不开氧气的情况下最大一个点1400ms左右。

C++ Code:

#include<bits/stdc++.h>
#define reg register
#define ll long long
struct node{
	ll l,r;
	int R,ls,rs,sz,len;
}a[6000050];
int n,m,q,rt[300005],sta[300005],top=0,cnt;
void update(int p){
	a[p].sz=a[a[p].ls].sz+a[a[p].rs].sz+1;
	a[p].len=a[a[p].ls].len+a[a[p].rs].len+(a[p].r-a[p].l+1);
}
inline int get(){
	reg int c=getchar(),d=0;
	for(;!isdigit(c);c=getchar());
	for(;isdigit(c);c=getchar())
	d=(d<<3)+(d<<1)+(c^‘0‘);
	return d;
}
int merge(int x,int y){
	if(!x||!y)return x|y;
	if(a[x].R<a[y].R){
		a[x].rs=merge(a[x].rs,y);update(x);
		return x;
	}
	a[y].ls=merge(x,a[y].ls);update(y);
	return y;
}
void split(int u,int k,int& x,int& y){
	if(k==0){
		x=0,y=u;
		return;
	}
	if(k==a[u].sz){
		x=u,y=0;
		return;
	}
	if(a[a[u].ls].sz>=k)split(a[u].ls,k,x,a[u].ls),y=u;else
	split(a[u].rs,k-a[a[u].ls].sz-1,a[u].rs,y),x=u;
	update(u);
}
int find(int u,int k){
	if(!k)return 0;
	if(a[a[u].ls].len<k&&k<=a[a[u].ls].len+(a[u].r-a[u].l+1))return a[a[u].ls].sz+1;
	if(k<=a[a[u].ls].len)return find(a[u].ls,k);
	return a[a[u].ls].sz+1+find(a[u].rs,k-a[a[u].ls].len-(a[u].r-a[u].l+1));
}
int build(){
	reg int x,pre;
	for(int i=1;i<=n;++i){
		a[x=++cnt]=(node){1ll*i*m,1ll*i*m,rand(),0,0,1,1};
		for(pre=0;top&&a[sta[top]].R>a[x].R;--top)update(pre=sta[top]);
		if(top)a[sta[top]].rs=x;
		a[x].ls=pre;sta[++top]=x;
	}
	while(top)update(sta[top--]);
	return sta[1];
}
int main(){
	srand(20170607);
	n=get(),m=get(),q=get();
	cnt=n;
	for(reg int i=1;i<=n;++i)a[rt[i]=i]=(node){1ll*(i-1)*m+1,1ll*i*m-1,rand(),0,0,1,m-1};
	rt[n+1]=build();
	while(q--){
		reg int x=get(),y=get();
		if(y==m){
			reg int xx,yy,zz;
			split(rt[n+1],x-1,xx,yy);
			split(yy,1,yy,zz);
			printf("%lld\n",a[yy].l);
			rt[n+1]=merge(xx,merge(zz,yy));
		}else{
			reg int p=find(rt[x],y),l1,l2,l3;
			split(rt[x],p-1,l1,l2);
			split(l2,1,l2,l3);
			reg ll ans=y-a[l1].len+a[l2].l-1;
			printf("%lld\n",ans);
			if(ans==a[l2].l){
				int l4,l5,l6;
				split(rt[n+1],x-1,l4,l5);
				split(l5,1,l5,l6);
				if(a[l2].len==1){
					rt[x]=merge(l1,merge(l3,l5));
					rt[n+1]=merge(l4,merge(l6,l2));
				}else{
					a[++cnt]=(node){ans,ans,rand(),0,0,1,1};
					--a[l2].len;++a[l2].l;
					rt[x]=merge(l1,merge(l2,merge(l3,l5)));
					rt[n+1]=merge(l4,merge(l6,cnt));
				}
			}else
			if(ans==a[l2].r){
				int l4,l5,l6;
				split(rt[n+1],x-1,l4,l5);
				split(l5,1,l5,l6);
				a[++cnt]=(node){ans,ans,rand(),0,0,1,1};
				--a[l2].len,--a[l2].r;
				rt[x]=merge(l1,merge(l2,merge(l3,l5)));
				rt[n+1]=merge(l4,merge(l6,cnt));
			}else{
				int l4,l5,l6;
				split(rt[n+1],x-1,l4,l5);
				split(l5,1,l5,l6);
				a[++cnt]=(node){a[l2].l,ans-1,rand(),0,0,1,int(ans-a[l2].l)};
				a[++cnt]=(node){ans,ans,rand(),0,0,1,1};
				a[l2].l=ans+1;
				a[l2].len=a[l2].r-a[l2].l+1;
				rt[x]=merge(l1,merge(cnt-1,merge(l2,merge(l3,l5))));
				rt[n+1]=merge(l4,merge(l6,cnt));
			}
		}
	}
	return 0;
}

 

[NOIP2017]列队

标签:log   printf   upd   bit   vijos   初始   void   空间复杂度   span   

原文地址:https://www.cnblogs.com/Mrsrz/p/8908085.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!