[NOIP2017]列隊
阿新 • • 發佈:2018-04-22
jos def spl include rand bsp 解題思路 times 平衡樹
題目:洛谷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]列隊