jzoj5478. 【NOIP2017提高组】列队

题意

一个n*m的矩阵,每个位置上有一个编号,每次取出一个编号(a,b),将(a,k) k>b左移一位,(z,m) z>a上移一位。 (左上角1,1),然后将取出的编号放在(n,m).
n,m<=3e5
询问数q<=3e5

分析

关注的是当前修改这一行和最后一列。

发现每一次修改都只是修改个别点的位置,这一行和最后一列其他点的相对位置是不变的
考虑n颗动态开点的线段树(1..m+预留的q个位置),还有一颗额外维护一下最后一列(1..n+q)。
每一次修改只需要
1. 线段树上二分找到第x个数
2. 加、改常数个点
因此线段树点数就是O(q log (n+q)),动态开点写的好可以开到6e6,写的炸就2e7.

时间复杂度是O(q log (n+q))

#include 
#include 
using namespace std;
const int N = 3e5+10,MXP = 5500000;
typedef long long ll;
ll n,m,q;
int root[N],lc[MXP],rc[MXP],size[MXP],tot;
int tail[N];
ll v[MXP];

void set(int &x,int l,int r,int tg,ll va=0) {
    if (!x) x=++tot;
    if (l==r) {
        v[x]=va;
        return;
    }
    if (tg<=(l+r>>1)) set(lc[x],l,l+r>>1,tg,va); 
    else set(rc[x],(l+r>>1)+1,r,tg,va);
    size[x]=size[lc[x]]+size[rc[x]];
}
ll kth(int &x,int l,int r,int tg,int &ret) {
    if (!x) x=++tot;
    if (l==r) {
        ret=l;
        size[x]=1;
        return v[x];
    }
    ll mid=l+r>>1,rrr;
    if (mid-l+1-size[lc[x]]>=tg) rrr=kth(lc[x],l,mid,tg,ret);
    else rrr=kth(rc[x],mid+1,r,tg-(mid-l+1-size[lc[x]]),ret);
    size[x]=size[lc[x]]+size[rc[x]];
    return rrr;
}

int main() {
    freopen("phalanx.in","r",stdin);
    freopen("phalanx.out","w",stdout);
    cin>>n>>m>>q;
    for (int i=1; i<=n; i++) tail[i]=m-1;
    tail[n+1]=n;

    int loc,x,y;
    ll tmp;
    for (int i=1; i<=q; i++) {
        scanf("%d %d",&x,&y);
        if (y==m) {
            tmp=kth(root[n+1],1,n+q,x,loc);
            if (loc<=n) tmp=loc*m;
            printf("%lld\n",tmp);
            set(root[n+1],1,n+q,++tail[n+1],tmp);
        } else {
            tmp=kth(root[x],1,m+q,y,loc);
            if (loc1)*m+loc;
            printf("%lld\n",tmp);
            set(root[n+1],1,n+q,++tail[n+1],tmp);
            tmp=kth(root[n+1],1,n+q,x,loc);
            if (loc<=n) tmp=loc*m;
            set(root[x],1,m+q,++tail[x],tmp);
        }
    }
    //cout<
}

你可能感兴趣的:(题解,数据结构)