[NOIp2017] 列队

毒瘤数据结构题

传送门:$>here<$


题意:给出一个$N*M$的方阵,每个人依次编号。有一个人离队,所有人先向左看齐,再向右看齐(挤占空位)。然后离队的人再回到队伍右下角站好。整个过程中人的编号不变。有$Q$次离队事件,给出离队位置,询问每一次离队的人的编号。

数据范围:$N,M,Q leq 3*10^5$


$Solution$

考虑用$N$棵线段树分别维护每一排的前$M-1$个人,第$N+1$棵线段树维护最后一列的人。我们的线段树是权值线段树,对于每一排(列也一样),第$l$个叶子对应原始排列的第$l$个人,值为0或1,维护的是这个位置的人是否还在这个位置。其实就是让权值线段树来干平衡树该干的事。

由于最多加入$Q$个人,所以线段树多开$Q$个位置初始化为0。刚开始建树时,对于前面有人的位置建1,否则建0

插入比较有趣,例如$(x,y)$这个人离队,那么首先删去第$x$棵线段树中排名为$y$的人,再删去第$N+1$棵线段树中排名为$x$的人。将后者插到第$x$棵线段树的最后,在将前者插入到第$N+1$棵线段树的最后。


现在有个问题,空间太大,根本不行……

考虑总共只有$Q$次离队,意味着很多节点是根本用不到的。考虑动态开点!

动态开点在这里的意思是,只开我们要用到的节点。对于那些没有用过的节点,形象一点,意味着这些点在队列中根本没有动过——即保持初始位置。这样我们就可以"算"出来了。询问到一个点,如果这个点还没有开过,就意味着它是满的。

$my code$

细节很多难以调试,我调试了好几天了(其实一个月前就开始做了……)

/*By DennyQi 2018*/
#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
#define int ll
const int MAXN = 1010;
const int INF = 1061109567;
inline int Max(const int a, const int b){ return (a > b) ? a : b; }
inline int Min(const int a, const int b){ return (a < b) ? a : b; }
inline int read(){
    int x = 0; int w = 1; register char c = getchar();
    for(; c ^ '-' && (c < '0' || c > '9'); c = getchar());
    if(c == '-') w = -1, c = getchar();
    for(; c >= '0' && c <= '9'; c = getchar()) x = (x<<3) + (x<<1) + c - '0'; return x * w;
}
int N,M,Q,numNode,x,y,L,psn1,psn2,mx_lst,cnt;
int rt[300010],lst[300010],idx[60000010],ls[6000010],rs[60000010],size[60000010];
inline int get_size(int l, int r, int x){
    if(x == N+1) return Max(Min(r,N)-l+1, 0);
    else return Max(Min(r,M-1)-l+1, 0);
}
int query(int& o, int l, int r, int k, int x){
    if(!o){
        o = ++cnt;
        size[o] = get_size(l, r, x);
        if(l == r){
            if(x == N+1){
                idx[o] = M * l;
            }
            else{
                idx[o] = M*(x-1) + l;
            }
        }
    }
    if(l == r){
        return idx[o];
    }
    int lsize, mid=(l+r)/2;
    if(!ls[o]) lsize = get_size(l,mid,x);
    else lsize = size[ls[o]];
    if(lsize >= k){
        return query(ls[o], l, mid, k, x);
    }
    else{
        return query(rs[o], mid+1, r, k-lsize, x);
    }
}
void modify(int& o, int l, int r, int k, int x, int p, int id, int pos){
    if(!o){
        o = ++cnt;
        size[o] = get_size(l, r, x);
        if(l == r){
            if(x == N+1){
                idx[o] = M * l;
            }
            else{
                idx[o] = M*(x-1) + l;
            }
        }
    }
    size[o] += p;
    if(l == r){
        if(p == 1){
            idx[o] = id;
        }
        else{
            if(p == -1 && x==N+1){
                psn2 = idx[o];
                idx[o] = 0;    
            }
        }
        return;
    }
    int lsize, mid=(l+r)/2;
    if(!ls[o]){
        lsize = get_size(l, mid, x);
    }else{
        lsize = size[ls[o]];
    }
    if(p == 1){
        if(mid >= pos){
            modify(ls[o], l, mid, k, x, p, id, pos);
        }
        else{
            modify(rs[o], mid+1, r, k, x, p, id, pos);
        }
        return;
    }
    if(lsize >= k){
        modify(ls[o], l, mid, k, x, p, id, pos);
    }
    else{
        modify(rs[o], mid+1, r, k-lsize, x, p, id, pos);
    }
}
signed main(){
//    freopen(".in","r",stdin);
    N = read(), M = read(), Q = read();
    for(int i = 1; i <= N; ++i){
        lst[i] = M-1;
    }
    lst[N+1] = N;
    for(int i = 1; i <= Q; ++i){
        x = read(), y = read();
        if(y == M){
            psn1 = query(rt[N+1], 1, N+Q, x, N+1);
            printf("%lld
", psn1);
            modify(rt[N+1], 1, N+Q, x, N+1, -1, psn1, 0);
            modify(rt[N+1], 1, N+Q, N, N+1, 1, psn1, ++lst[N+1]);
        }
        else{
            psn1 = query(rt[x], 1, M+Q, y, x);
            printf("%lld
", psn1);
            modify(rt[x], 1, M+Q, y, x, -1, 0, 0);
            modify(rt[N+1], 1, N+Q, x, N+1, -1, 0, 0);
            modify(rt[x], 1, M+Q, M-1, x, 1, psn2, ++lst[x]);
            modify(rt[N+1], 1, N+Q, N, N+1, 1, psn1,++lst[N+1]);
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/qixingzhi/p/9911324.html