P3988 [SHOI2013]发牌

  • 题意:要求就是有 \(n <= 7e5\) 长度的序列,然后要求给 \(n\) 个操作,每次删除一个数,删除的是在当前序列中,讲前 \(po_i\) 个数移到后面后的序列中最前面的数。
  • 题解:权值线段树求第 \(k\) 大,然后这个 \(k\) 是根据删除的位置,一次一次找到规律推出来。
  • 代码:
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
const ll N = 7e5 + 9;

struct segmentTree {
    struct node {
        int l, r, data, L, R;
    }tr[N<<4];
    void pushup(int p){tr[p].data = tr[tr[p].l].data + tr[tr[p].r].data;}
    inline void build(int l, int r, int p) {
        tr[p].l = p << 1;
        tr[p].r = p << 1 | 1;
        tr[p].L = l, tr[p].R = r;
        if (l ==r){tr[p].data = 1;return;}
        int mid = l + r >> 1;
        build(l, mid, tr[p].l);
        build(mid + 1, r, tr[p].r);
        pushup(p);
        return;
    }
    inline void add(int pos, int p, int num) {
        //cout << tr[p].L << " " << tr[p].R << endl;
        tr[p].data += num;
        int mid = tr[p].L + tr[p].R  >> 1;
        if (tr[p].L == tr[p].R)return;
        if (pos <= mid)
        add(pos, tr[p].l, num);
        else add(pos, tr[p].r, num);
    }
    inline int ask(int l, int r, int p, int k) {
        int cnt = tr[tr[p].l].data;
        //cout << l << " " << r << " " << cnt << endl;
        if (l == r)return l;
        int mid = l + r >> 1;
        if (cnt >= k)return ask(l, mid, tr[p].l, k);
        else return ask(mid + 1, r, tr[p].r, k-cnt);
    }
}T;
signed main() {
    int n;scanf("%d", &n);
    T.build(1, n, 1);
    int cnt = n;
    int pos = 1;
    for (int i = 1; i <= n; i ++) {
        int res;
        int x;scanf("%d", &x);
        int q = (pos + x) % cnt;
        if (q == 0)pos = q = cnt;
        else pos = q;
        res = T.ask(1, n, 1, q);
        printf("%d\n", res);
        T.add(res, 1, -1);
        cnt--;
    }
}
posted @ 2021-04-28 17:22  u_yan  阅读(49)  评论(0编辑  收藏  举报