[NOIP2017] 列隊
阿新 • • 發佈:2020-07-22
題意:
有一個$n\times m$的方陣,方陣裡的人從上到下,從左到右編號為$1-nm$。
有q次操作,每次操作分為如下步驟:
- 先將位置$(x,y)$的人拿出來;
- 然後讓$(x,y)$右邊的人整體左移補齊空位;
- 再讓$(x,m)$下邊的人整體上移補齊空位;
- 最後將一開始拿出來的人放到$(n,m)$。
請你求出每次操作拿出來的人的編號。
$n,m,q\leq 3\times 10^{5}$。
題解:
首先考場上80分是送的,注意到這是個裸的區間分裂/合併,所以Splay做法也是送的,但是會被卡。
仔細考察一下這個有點複雜的操作,我們可以把它分成兩個部分。
- 在第x行刪一個數,再往後面填一個數。
- 在第m列刪一個數,再往後面填一個數。
那麼我們只要能解決“在一個序列裡刪一個數,再往後面填一個數”就行了。
如果Splay的話是“刪一個區間,填一個區間”,有點大材小用,我們考慮樹狀陣列做一下。
有一個簡單的套路:不考慮填的數是什麼,只考慮下標,相當於每次單點-1,查詢第一個字首和為x的位置。
單點修改直接做,查詢時把區間補成$[1,2^{n}]$,由於樹狀陣列每層只有左邊那個塊,直接套用線段樹區間第k大的做法即可。
相當於我們把所有要填進來的數都填進來成為一個新序列,那麼$qry(x)$就是當前x這個位置對應的新序列中的下標。
注意到下標和填進來了什麼沒關係,所以我們對每個詢問預處理出它那一行的$qry(x)$和最後一列的$qry(y)$,然後直接拿vector填數就行了。
複雜度$O(q\log{n})$,細節有點多,考場上還是寫80分吧。
套路:
- 一個複雜的操作$\rightarrow$拆成多個簡單的小操作之和。
- 單點刪除$\rightarrow$樹狀陣列維護。
程式碼:
#include<bits/stdc++.h> #define maxn 2000005 #define maxm 500005 #define inf 0x7fffffff #define ll long long #define rint register ll #define debug(x) cerr<<#x<<": "<<x<<endl #define列隊fgx cerr<<"--------------"<<endl #define dgx cerr<<"=============="<<endl using namespace std; struct Ques{ll x,y,rx,ry;}Q[maxn]; vector<ll> ins[maxn],vc[maxn]; inline ll read(){ ll x=0,f=1; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } struct BIT{ ll C[maxn]; inline void clear(){memset(C,0,sizeof(C));} inline ll lowbit(ll x){return x&(-x);} inline void add(ll x,ll y,ll n){for(ll i=x;i<=n;i+=lowbit(i))C[i]+=y;} inline ll qry(ll x,ll n){ll l=1,r=n;while(l<r){ll mid=l+r>>1;if(C[mid]<x)l=mid+1,x-=C[mid];else r=mid;}return l;} }; int main(){ ll n=read(),m=read(),q=read(); for(ll i=1;i<=q;i++){ ll x=read(),y=read(); Q[i].x=x,Q[i].y=y,vc[Q[i].x].push_back(i); } BIT tp; tp.clear(); ll N=1; while(N<m+q) N<<=1; for(ll i=1;i<=N;i++) tp.add(i,1,N); for(ll i=1;i<=n;i++){ for(ll j=0;j<vc[i].size();j++){ ll id=vc[i][j]; if(Q[id].y==m) continue; Q[id].ry=tp.qry(Q[id].y,N),tp.add(Q[id].ry,-1,N); } for(ll j=0;j<vc[i].size();j++){ ll id=vc[i][j]; if(Q[id].y==m) continue; tp.add(Q[id].ry,1,N); } } tp.clear(); N=1; while(N<n+q) N<<=1; for(ll i=1;i<=N;i++) tp.add(i,1,N); for(ll i=1;i<=q;i++) Q[i].rx=tp.qry(Q[i].x,N),tp.add(Q[i].rx,-1,N); for(ll i=1;i<=n;i++) ins[n+1].push_back(i*m); for(ll i=1;i<=q;i++){ if(Q[i].y==m){ printf("%lld\n",ins[n+1][Q[i].rx-1]); ins[n+1].push_back(ins[n+1][Q[i].rx-1]); } else{ if(Q[i].ry>m-1){ printf("%lld\n",ins[Q[i].x][Q[i].ry-m]); ins[n+1].push_back(ins[Q[i].x][Q[i].ry-m]); } else{ printf("%lld\n",(Q[i].x-1)*m+Q[i].ry); ins[n+1].push_back((Q[i].x-1)*m+Q[i].ry); } ins[Q[i].x].push_back(ins[n+1][Q[i].rx-1]); } } return 0; }