1. 程式人生 > 實用技巧 >[NOIP2017] 列隊

[NOIP2017] 列隊

題意:

有一個$n\times m$的方陣,方陣裡的人從上到下,從左到右編號為$1-nm$。

有q次操作,每次操作分為如下步驟:

  1. 先將位置$(x,y)$的人拿出來;
  2. 然後讓$(x,y)$右邊的人整體左移補齊空位;
  3. 再讓$(x,m)$下邊的人整體上移補齊空位;
  4. 最後將一開始拿出來的人放到$(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; }
列隊