[NOIP2017 提高组] 列队
我有病吧我挑这个题做。
题意:
$ n,m,q\le 3e5 $
解题思路:
一眼看上去相当没有头绪。
但如果仔细观察的话会发现这种操作本质上是改变某一个编号的位置,将其放在序列最后并将序列整体移动,不难想到用平衡树。
然后暴力打完就可以跑路了,实现好的话能得 \(80pts\) 。
讲真如果考场上真出了这道题按我来至少得来上两个小时,性价比极差,还不如打暴力。
设每次离队的人的位置为 \((H,L)\) 。
考虑对最后一列建一棵平衡树 \(A\) ,以此来维护第 \(m\) 列的编号。\(L=m\) 时直接按照套路分裂合并即可。现在重要的是如何得到 \(L\ne m\) 的情况。
观察到如果 \(L\ne m\) ,那么第 \(m\) 列会在第 \(H\) 行的方向上移动一个位置(这个位置脱离了第 \(m\) 列)。发现移动次数不会超过 \(q\) 个,可以对脱离第 \(m\) 列的位置建 \(n\) 棵平衡树 \(B\) 。第 \(i\) 棵平衡树 \(B_i\) 维护有多少位置是从第 \(m\) 列传到第 \(i\) 行的,当 \(L>m-1-siz_i\) 的时候分裂一个位置到 \(A\) ,再从 \(A\) 上分裂一个位置到 \(B_i\) 即可。
如果操作位置并非在平衡树 \(B_i\) 上,说明这个位置一定不是第 \(m\) 列传递到第 \(i\) 行的,而是原本位于第 \(i\) 行的位置。
这时发现把第 \(i\) 行的所有位置记录下来基本不可能。
正难则反,我们仍能建 \(n\) 棵平衡树 \(C\) 记录第 \(i\) 行都删除了那些位置(或者说都有哪些同学离队了)。在离队的位置上维护第 \(i\) 行现有人数的前缀和。查询时在 \(C_i\) 上分裂,并对查到的位置新建两个节点——一个合并在 \(C_i\) 上,表示这个位置被删过了,另一个合并在 \(A\) 上,表示在 \((n,m)\) 的位置加回来,然后将 \(A\) 在第 \(i\) 行的位置删除,移动到 \(B_i\) 上。
然后这题就做完了,复杂度 \(O(n\log n)\),注意开long long
。
细节很多,人调得很麻。
代码:
#include <cstdio>
#include <random>
#include <algorithm>
#define Reg register
#define int long long
#define lson(rt) tr[rt].ls
#define rson(rt) tr[rt].rs
using namespace std;
const int maxn=301000;
int n,m,q,tot,root,RT[2][maxn];
struct FHQ_TREAP{
int siz,lip,lz,dat,ls,rs,val,sum;
}tr[maxn*3];
inline int read(){
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') w=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
s=(s<<1)+(s<<3)+(ch^48);
ch=getchar();
}
return s*w;
}
//这题应该是有脑平衡树
//但考场上最优策略都会选择打暴力罢
inline int Newnode(int val,int lip){
tr[++tot].val=val;
tr[tot].dat=rand();
tr[tot].siz=1;
tr[tot].lip=lip;
return tot;
}
inline void Dosth(int rt,int v){
if(!rt) return;
tr[rt].sum+=v;
tr[rt].lz+=v;
return;
}
inline void pushdown(int rt){
if(!tr[rt].lz) return;
Dosth(lson(rt),tr[rt].lz);
Dosth(rson(rt),tr[rt].lz);
tr[rt].lz=0;
}
inline void pushup(int rt){
tr[rt].siz=tr[lson(rt)].siz+tr[rson(rt)].siz+1;
}
inline int Merge(int u,int v){
if(!u||!v) return u|v;
if(tr[u].dat<tr[v].dat){
pushdown(u);
rson(u)=Merge(rson(u),v);
pushup(u);
return u;
}else{
pushdown(v);
lson(v)=Merge(u,lson(v));
pushup(v);
return v;
}
}
inline void Splitsiz(int rt,int &x,int &y,int k){
if(!rt) return x=0,y=0,void();
pushdown(rt);
if(k>tr[lson(rt)].siz) x=rt,Splitsiz(rson(rt),rson(rt),y,k-tr[lson(rt)].siz-1);
else y=rt,Splitsiz(lson(rt),x,lson(rt),k);
pushup(rt);
}
inline void Splitval(int rt,int &x,int &y,int v){
if(!rt) return x=0,y=0,void();
pushdown(rt);
if(tr[rt].sum<=v) x=rt,Splitval(rson(rt),rson(rt),y,v);
else y=rt,Splitval(lson(rt),x,lson(rt),v);
pushup(rt);
}
inline void Debug(int rt){
if(!rt) return;
printf("rt=%lld lson=%lld rson=%lld siz=%lld sum=%lld val=%lld lip=%lld\n",rt,lson(rt),rson(rt),tr[rt].siz,tr[rt].sum,tr[rt].val,tr[rt].lip);
pushdown(rt);
Debug(lson(rt));
Debug(rson(rt));
}
inline int Findid(int rt,int S){
if(!rt) return 0;
if(S<=tr[lson(rt)].siz) return Findid(lson(rt),S);
else if(S==tr[lson(rt)].siz+1) return rt;
else return Findid(rson(rt),S-tr[lson(rt)].siz-1);
}
signed main(){
srand(time(0));
n=read(),m=read(),q=read();
for(Reg int i=1;i<=n;++i) root=Merge(root,Newnode(i*m,0));
int cnt=0;
while(q--){
int H=read(),L=read();
++cnt;
if(L==m){
int x=0,y=0,z=0;
Splitsiz(root,x,y,H);
Splitsiz(x,x,z,H-1);
printf("%lld\n",tr[z].val);
y=Merge(y,z);
root=Merge(x,y);
continue;
}
if(!RT[0][H]&&!RT[1][H]){
int x=0,y=0,z=0,Del=0,Add=0;
Splitsiz(root,x,y,H);
Splitsiz(x,x,z,H-1);
RT[0][H]=Merge(RT[0][H],z);
Del=Newnode((H-1)*m+L,L);
Add=Newnode((H-1)*m+L,0);
tr[Del].sum=L-1;
printf("%lld\n",(H-1)*m+L);
RT[1][H]=Merge(RT[1][H],Del);
y=Merge(y,Add);
root=Merge(x,y);
continue;
}
if(L>m-1-tr[RT[0][H]].siz){
int x=0,y=0,z=0,o=0,t=0,g=0;
int rtsiz=m-1-tr[RT[0][H]].siz;
Splitsiz(RT[0][H],x,y,L-rtsiz);
Splitsiz(x,x,z,L-rtsiz-1);
printf("%lld\n",tr[z].val);
Splitsiz(root,o,t,H);
Splitsiz(o,o,g,H-1);
x=Merge(x,y);
RT[0][H]=Merge(x,g);
t=Merge(t,z);
root=Merge(o,t);
}else{
int x=0,y=0,z=0,o=0,t=0,g=0,Del=0,Add=0,pos=0;
//if(cnt==91) Debug(RT[1][H]);
Splitval(RT[1][H],x,y,L);
//Debug(x);
int maxp=Findid(x,tr[x].siz);
if(!maxp) pos=L;
else{
if(L!=tr[maxp].sum) pos=L-tr[maxp].sum+tr[maxp].lip;
else{
int w=0,v=0;
Splitval(x,x,w,L-1);
maxp=Findid(x,tr[x].siz);
pos=L-tr[maxp].sum+tr[maxp].lip;
y=Merge(w,y);
}
}
//if(cnt==91)printf("xsum=%lld lip=%lld pos=%lld\n",tr[x].sum,tr[x].lip,pos);
//if(cnt==91)printf("ysum=%lld lip=%lld pos=%lld\n",tr[y].sum,tr[y].lip,pos);
Del=Newnode((H-1)*m+pos,pos);
Add=Newnode((H-1)*m+pos,0);
printf("%lld\n",(H-1)*m+pos);
tr[Del].sum=pos-(tr[maxp].lip-tr[maxp].sum+1);
//if(cnt==91) printf("!!%lld\n",tr[Del].sum);
Dosth(y,-1);
x=Merge(x,Del);
RT[1][H]=Merge(x,y);
Splitsiz(root,o,t,H);
Splitsiz(o,o,g,H-1);
RT[0][H]=Merge(RT[0][H],g);
t=Merge(t,Add);
root=Merge(o,t);
}
}
return 0;
}