1. 程式人生 > >[NOIP2017]列隊

[NOIP2017]列隊

jos def spl include rand bsp 解題思路 times 平衡樹

題目:洛谷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]列隊