【NOIP2017】【UOJ334】【LOJ2319】列队

【题目链接】

【前置技能】

  • 动态开节点线段树

【题解】

  • 本题好像有什么树状数组就能做的解法,但我只会线段树和Splay的大力乱搞,毕竟思考起来比较直观一点。以下是线段树做法,代码比较丑,懒得改了。
  • 仔细观察发现一次变动影响的点不是很多,那我们就建立支持单点修改的线段树。对每一行的前 m1 m − 1 个点维护两棵线段树,对最后一列维护两棵线段树。其中第一棵线段树表示的是原来的人,第二棵线段树表示的是修改后进来的人。一开始两棵线段树都是空的,表示每个位置上都是原来的人。
  • 在某一个人 (x,y) ( x , y ) 移动的时候,先在第一棵线段树上看一下未移动的人是否大于等于 y y 个,若满足,则在第一棵线段树上二分找到第y个位置并打上删除标记;否则在第二棵线段树上二分找到答案并打上删除标记。然后用同样的方法找到维护列的线段树上第 x x 个数,把它打上删除标记,并把这个数加到这一行的尾部。最后要把该询问的答案加到最后一列的尾部。
  • 注意移动的人就在最后一列的情况要特判一下,直接在维护列的线段树上修改即可。
  • 因为每次最多修改O(logN)个节点,动态开节点,空间复杂度 O(QlogN) O ( Q l o g N ) 。时间复杂度 O(QlogN) O ( Q l o g N )

【代码】

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define LL  long long
#define MAXN    300100
using namespace std;
LL n, m, Q;

template <typename T> void chkmin(T &x, T y){x = min(x, y);}
template <typename T> void chkmax(T &x, T y){x = max(x, y);}
template <typename T> void read(T &x){
    x = 0; int f = 1; char ch = getchar();
    while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();}
    while (isdigit(ch)) {x = x * 10 + ch - '0'; ch = getchar();}
    x *= f;
}

struct Segment_TreeI{
    struct info{int ls, rs, sum;}a[MAXN * 100];
    int root[MAXN], cnt, n;
    void push_up(int pos){
        a[pos].sum = a[a[pos].ls].sum + a[a[pos].rs].sum;
    }
    void init(int x){
        n = x, cnt = 0;
    }
    void modify(int &pos, int l, int r, int p, int d){
        if (!pos) pos = ++cnt;
        if (l == r) {
            a[pos].sum += d;
            return;
        }
        int mid = (l + r) >> 1;
        if (p <= mid) modify(a[pos].ls, l, mid, p, d);
        else modify(a[pos].rs, mid + 1, r, p, d);
        push_up(pos);
    }
    void modify(int T, int p, int d){
        modify(root[T], 1, n, p, d);
    }
    LL query(int pos, int l, int r, int k){
        if (l == r) return l;
        int mid = (l + r) >> 1;
        int tmp = (mid - l + 1) - a[a[pos].ls].sum;
        if (tmp >= k) return query(a[pos].ls, l, mid, k);
        else return query(a[pos].rs, mid + 1, r, k - tmp);
    }
    LL query(int T, int k){
        return query(root[T], 1, n, k);
    }
    int size(int T){
        return a[root[T]].sum;
    }
}sgtI;

struct Segment_TreeII{
    struct info{int ls, rs, sum; LL data;}a[MAXN * 100];
    int root[MAXN], cnt, n, used[MAXN];
    void push_up(int pos){
        a[pos].sum = a[a[pos].ls].sum + a[a[pos].rs].sum;
    }
    void init(int x){
        n = x, cnt = 0;
    }
    void modify(int &pos, int l, int r, int p, LL d){
        if (!pos) pos = ++cnt;
        if (l == r) {
            a[pos].data = d;
            if (d) a[pos].sum = 1;
                else a[pos].sum = 0;
            return;
        }
        int mid = (l + r) >> 1;
        if (p <= mid) modify(a[pos].ls, l, mid, p, d);
        else modify(a[pos].rs, mid + 1, r, p, d);
        push_up(pos);
    }
    void modify(int T, int p, LL d){
        modify(root[T], 1, n, p, d);
    }
    LL query(int pos, int l, int r, int k){
        if (l == r) return a[pos].data;
        int mid = (l + r) >> 1;
        int tmp = a[a[pos].ls].sum;
        if (tmp >= k) return query(a[pos].ls, l, mid, k);
        else return query(a[pos].rs, mid + 1, r, k - tmp);
    }
    LL query(int T, int k){
        return query(root[T], 1, n, k);
    }
    int get(int pos, int l, int r, int k){
        if (l == r) return l;
        int mid = (l + r) >> 1;
        int tmp = a[a[pos].ls].sum;
        if (tmp >= k) return get(a[pos].ls, l, mid, k);
        else return get(a[pos].rs, mid + 1, r, k - tmp);
    }
    int get(int T, int k){
        return get(root[T], 1, n, k);
    }
}sgtII;

int main(){
    read(n), read(m), read(Q); 
    sgtI.init(max(m, n));
    sgtII.init(Q);
    while (Q--){
        int x, y; read(x), read(y);
        if (y == m){
            int tmp = n - sgtI.size(n + 1); LL id;
            if (tmp >= x) id = 1ll * sgtI.query(n + 1, x) * m;
                else id = sgtII.query(n + 1, x - tmp);
            printf("%lld\n", id);
            if (tmp >= x) sgtI.modify(n + 1, id / m, 1);
                else sgtII.modify(n + 1, sgtII.get(n + 1, x - tmp), 0);
            sgtII.modify(n + 1, ++sgtII.used[n + 1], id);
        } else {
            int tmp = m - sgtI.size(x) - 1;
            LL id, ID;
            if (tmp >= y) id = 1ll * sgtI.query(x, y) + 1ll * (x - 1) * m;
            else id = sgtII.query(x, y - tmp);
            ID = id;
            printf("%lld\n", ID);
            if (tmp >= y) sgtI.modify(x, id - 1ll * (x - 1) * m, 1);
                else sgtII.modify(x, sgtII.get(x, y - tmp), 0);
            tmp = n - sgtI.size(n + 1);
            if (tmp >= x) id = 1ll * sgtI.query(n + 1, x) * m;
                else id = sgtII.query(n + 1, x - tmp);
            sgtII.modify(x, ++sgtII.used[x], id);
            if (tmp >= x) sgtI.modify(n + 1, id / m, 1);
                else sgtII.modify(n + 1, sgtII.get(n + 1, x - tmp), 0);
            sgtII.modify(n + 1, ++sgtII.used[n + 1], ID);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值