[NOIP2017]列队
Posted Mrsrz
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[NOIP2017]列队相关的知识,希望对你有一定的参考价值。
题目:洛谷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]列队的主要内容,如果未能解决你的问题,请参考以下文章