[NOIP2017]列队

description

洛谷

solution

一开始的想法是写\(O(nlogn)\)拆点\(Splay\)——这玩意对我来说根本不可做。

于是想了一个线段树动态开点+\(Splay\)的做法
然后就调了一个晚自习,最后发现还是\(O(nlog^2n)\)的,不开\(O2\)最后一个点\(1975ms\)

这里是本人的解法

首先考虑\(n=1\)的情况。
此时的操作是从有序队列中取出一个节点,然后放到序列的末尾。

对于序列末尾的元素,使用\(Splay\)维护位置和编号即可。
对于不是序列末尾的元素,我们发现似乎可以使用线段树维护其编号。
举个例子:

     a:1 2 3 4 5 6 7 8
(1,5)->1 2 3 4[6 7 8]5

我们发现除了一个被放在末尾的元素\((5)\)以外,其余\([5,7]\)位置的编号都\(+1\)
但真的能直接这样维护吗?我们继续看:

     a:1 2 3 4 5 6 7 8
(1,5)->1 2 3 4[6 7 8]5
(1,3)->1 2[4 6 7 8]5 3

这时我们发现\(a\)数组的\([3,6]\)位置的编号有的\(+1\),有的\(+2\),
因此直接维护编号是不可行的。

于是我们考虑维护每个编号的元素对应出现的位置\(p\):

       a:1 2 3 4 5 6 7 8
       p:1 2 3 4 5 6 7 8
(1,5)->a:1 2 3 4 6 7 8 5
       p:1 2 3 4[x 5 6 7]

由于放到序列末尾的\(5\)已经使用了\(splay\)维护,因此我们不需要再维护其位置信息。
继续看:

       a:1 2 3 4 5 6 7 8
       p:1 2 3 4 5 6 7 8
(1,5)->a:1 2 3 4 6 7 8 5
       p:1 2 3 4[4 5 6 7]
(1,3)->a:1 2 4 6 7 8 5 3
       p:1 2[2 3 3 4 5 6]

可以发现\(p\)数组的维护是符合要求的。
找到第\(i\)个位置的元素时,需要在\(p\)中二分找到第一个\(==i\)的元素,
该元素的位置就是我们要找的编号。
举个例子:

       a:1 2 3 4 5 6 7 8
       p:1 2 3 4 5 6 7 8
(1,5)->a:1 2 3 4 6 7 8 5
       p:1 2 3 4[4 5 6 7]

我们在第一次修改之后,找第\(5\)个位置的元素,
那么在\(p\)数组中找到值为\(5\)的第一个元素。
这个元素的位置为\(6\),因此原队列中第\(5\)个元素的编号是\(6\)
因此相当于区间修改+单点查询

\(n\)队的时候使用线段树动态开点,
由于要在二分查找里面套线段树查询,因此线段树就是\(O(nlog^2n)\)的了...
还有常数大的\(Splay\),我都不知道怎么卡过的...

更加优秀的\(nlogn\)做法

仍然使用线段树。但线段树维护的不再是节点位置,而是区间内被删除的数的个数。
这样我们就可以做到查询区间第\(k\)大了。
具体来说,查找到一个区间时,
左节点的数的个数相当于(左区间长度-左区间内已经被删掉的元素个数)
而这个删除个数是可以通过动态开点来维护的。
这里相当于单点修改+二分查找
因此,线段树查询非询问部分编号的时间复杂度为\(O(nlogn)\)

我们把这种思想应用到对于询问点的处理上;
对于后方的询问也使用动态开点线段树,插点的同时记录删除点的个数;
同样是单点修改+二分查找,
时间复杂度同为\(O(nlogn)\)

这样一来最大点就只要跑\(738ms\)

Code

本人的线段树+\(Splay\)做法

#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<iomanip>
#include<cstring>
#include<complex>
#include<vector>
#include<cstdio>
#include<string>
#include<bitset>
#include<ctime>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#define Cpy(x,y) memcpy(x,y,sizeof(x))
#define Set(x,y) memset(x,y,sizeof(x))
#define FILE "a"
#define mp make_pair
#define pb push_back
#define RG register
#define il inline
using namespace std;
typedef unsigned long long ull;
typedef vector<int>VI;
typedef long long ll;
typedef double dd;
const dd eps=1e-7;
const int N=600010;
const int M=1000010;
const int inf=2147483647;
const ll INF=1e18+1;
const ll P=100000;
il ll read(){
	RG ll data=0,w=1;RG char ch=getchar();
	while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
	if(ch=='-')w=-1,ch=getchar();
	while(ch<='9'&&ch>='0')data=data*10+ch-48,ch=getchar();
	return data*w;
}

il void file(){
	srand(time(NULL)+rand());
	freopen(FILE".in","r",stdin);
	freopen(FILE".out","w",stdout);
}

/*********************************************************************/

int n,m,q;
int cnt,srt[N],ss[2][20*N],slz[20*N];
#define mid ((l+r)>>1)
void mod(int &i,int l,int r,int x,int y,int d){
	if(x>y)return;if(!i)i=++cnt;
	if(x<=l&&r<=y){slz[i]+=d;return;}
	if(x<=mid)mod(ss[0][i],l,mid,x,y,d);
	if(mid<y)mod(ss[1][i],mid+1,r,x,y,d);
}
int qry(int i,int l,int r,int p){
	if(!i||l==r)return slz[i];
	if(p<=mid)return slz[i]+qry(ss[0][i],l,mid,p);
	else return slz[i]+qry(ss[1][i],mid+1,r,p);
}
/*********************************************************************/

int rt[N],s[2][N],fa[N],sz[N];ll rk[N];int tot;
int cal[N],top;
int newnode(){return top?cal[top--]:++tot;}

#define isr(i) (s[1][fa[i]]==i)
il void update(int i){if(i)sz[i]=sz[s[0][i]]+sz[s[1][i]]+1;}
il void rot(int i){
	RG int j=fa[i],k=fa[j];RG bool b=isr(i);
	if(k)s[isr(j)][k]=i;fa[i]=k;
	if(s[!b][i])fa[s[!b][i]]=j;s[b][j]=s[!b][i];
	s[!b][i]=j;if(j)fa[j]=i;update(j);
}
il void splay(int i,int a,int k){
	for(RG int j=fa[i];j!=a;rot(i),j=fa[i]){
		if(fa[j])isr(i)^isr(j)?rot(i):rot(j);
		if(fa[i]==a)break;
	}
	if(!a)rt[k]=i;update(i);
}
il ll find(int k,int p){
	RG int i=rt[k],pre;
	while(2333){
		if(!i)break;
		if(p<=sz[s[0][i]])i=s[0][i];
		else if(p==sz[s[0][i]]+1){
			splay(i,0,k);
			pre=s[0][i];while(s[1][pre])pre=s[1][pre];
			if(pre){
				splay(pre,i,k);rt[k]=pre;
				fa[s[1][i]]=pre;s[1][pre]=s[1][i];
				s[0][i]=s[1][i]=fa[pre]=0;update(pre);
			}
			else{
				rt[k]=s[1][i];fa[s[1][i]]=0;s[1][i]=0;
			}
			cal[++top]=i;return rk[i];
		}
		else p-=sz[s[0][i]]+1,i=s[1][i];
	}
}
il void insert(int k,ll r){
	RG int i=rt[k],nw;
	while(s[1][i])i=s[1][i];
	if(!i)rt[k]=nw=newnode();
	else splay(i,0,k),s[1][i]=nw=newnode();
	fa[nw]=i;sz[nw]=1;rk[nw]=r;s[0][nw]=s[1][nw]=0;
	update(i);
}
/*********************************************************************/

int sq[N];
il int lowerbound(int k,int p){
	RG int l=1,r=(k==n+1?n:m-1),len=r,as;
	while(l<r){
		as=(mid+qry(srt[k],1,len,mid));
		if(as<p)l=mid+1;else r=mid;
	}
	return l;
}
il ll query(int k,int p){
	RG int len=(k==n+1?n:m-1);ll r;
	if(p>len-sq[k])r=find(k,p+sq[k]-len);
	else{
		sq[k]++;
		if(k==n+1){
			r=lowerbound(k,p);
			mod(srt[k],1,n,r,n,-1);
			r=r*m;
		}
		else{
			r=lowerbound(k,p);
			mod(srt[k],1,m-1,r,m-1,-1);
			r=1ll*(k-1)*m+r;
		}
	}
	return r;
}
/*********************************************************************/

int main()
{
	n=read();m=read();q=read();
	for(RG int i=1,x,y;i<=q;i++){
		RG ll r;
		x=read();y=read();
		
		r=query(n+1,x);
		if(y!=m){
			insert(x,r);
			r=query(x,y);
		}
		insert(n+1,r);
		printf("%lld\n",r);
	}
	return 0;
}

更好的线段树做法

#include<bits/stdc++.h>
#define RG register
#define il inline
using namespace std;
typedef long long ll;
const int N=300010;
il ll read(){
	RG ll data=0,w=1;RG char ch=getchar();
	while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
	if(ch=='-')w=-1,ch=getchar();
	while(ch<='9'&&ch>='0')data=data*10+ch-48,ch=getchar();
	return data*w;
}
int n,m,q,x,y,sz[N];
int rt[2][N],s[2][40*N],sum[40*N],tot;ll id[40*N];
#define mid ((l+r)>>1)
ll query(int &i,int l,int r,int p,int k){
	if(!i)i=++tot;sum[i]++;if(l==r)return k?id[i]:l;
	if(p<=(mid-l+1)-sum[s[0][i]])return query(s[0][i],l,mid,p,k);
	else return query(s[1][i],mid+1,r,p-((mid-l+1)-sum[s[0][i]]),k);
}
void insert(int &i,int l,int r,int p,ll v){
	if(!i)i=++tot;if(l==r){id[tot]=v;return;}
	if(p<=(mid-l+1)-sum[s[0][i]])insert(s[0][i],l,mid,p,v);
	else insert(s[1][i],mid+1,r,p-((mid-l+1)-sum[s[0][i]]),v);
}
il ll find(int k,int len,int p){
	if(p>len-sz[k]){return query(rt[1][k],1,q,p+(sz[k]--)-len,1);}
	if(k==n+1)return 1ll*m*query(rt[0][k],1,len,p,0);
	else return 1ll*(k-1)*m+query(rt[0][k],1,len,p,0);
}
ll work(int x,int y){
	RG ll r1=find(n+1,n,x),r2;
	if(y!=m){
		r2=find(x,m-1,y);
		sz[x]++;
		insert(rt[1][x],1,q,sz[x],r1);
		swap(r1,r2);
	}
	sz[n+1]++;
	insert(rt[1][n+1],1,q,sz[n+1],r1);
	return r1;
}
int main()
{
	n=read();m=read();q=read();
	for(RG int i=1;i<=q;i++){
		x=read();y=read();
		printf("%lld\n",work(x,y));
	}
	return 0;
}

posted @ 2018-09-21 15:34  cjfdf  阅读(171)  评论(0编辑  收藏  举报