题目:洛谷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;
}