[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;
}