[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;
}
列队

 

posted @ 2020-07-22 21:01  Fugtemypt  阅读(145)  评论(0编辑  收藏  举报