【主席树】CF813 E. Army Creation

发布时间 2023-08-26 22:02:42作者: blockche

【主席树】CF813 E. Army Creation

题目链接:https://codeforces.com/contest/813/problem/E

题意

多次询问,求一个区间内,所有数个数的总和,但相同的数最多被计算k次,强制在线。

题解

这道题和牛客一道题很像,是那道题的加强版,链接在这:题解链接

那道题可以离线,所以离线处理+树状数组可以做,但这道题强制在线,于是我们掏出主席树。

可以发现,在区间 0~n-1 上依次建主席树的历史版本,然后就可以把每个历史版本都当作一个遍历到那个位置的树状数组来用了。

其他步骤和我前面说的那道青春版的题很像,也是尽量选最靠近r的左边的k个数最优,超出部分直接删掉就好了。

代码

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;

struct Node {
    Node *l;
    Node *r;
    int v;
};
Node *add(Node *p, int l, int r, int x, int d) {
    Node *np = new Node();
    if (p) *np = *p;
    np->v += d;
    if (l == r) return np;
    int m = (l + r) / 2;
    if (x <= m) {
        np->l = add(np->l, l, m, x, d);
    } else {
        np->r = add(np->r, m + 1, r, x, d);
    }
    return np;
}
int query(Node *p, int l, int r, int x) {
    if (r < x) return 0;
    if (x <= l) return p ? p->v : 0;
    int ans = 0;
    int m = (l + r) / 2;
    ans += query(p ? p->l : nullptr, l, m, x);
    ans += query(p ? p->r : nullptr, m + 1, r, x);
    return ans;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n, k;
    cin >> n >> k;
    vector<int> a(n);
    for (int i = 0; i < n; i++) {
        cin >> a[i];
    }

    int mx = *max_element(a.begin(), a.end());
    vector<Node *> rt(n + 1);
    vector<vector<int>> pos(mx + 1);
    for (int i = 0; i < n; i++) {
        rt[i + 1] = add(rt[i], 0, n - 1, i, 1);
        if (pos[a[i]].size() >= k) {
            rt[i + 1] = add(rt[i + 1], 0, n - 1, *(pos[a[i]].end() - k), -1);
        }
        pos[a[i]].push_back(i);
    }

    int ans = 0;
    int q;
    cin >> q;
    while (q--) {
        int l, r;
        cin >> l >> r;
        l = (l + ans) % n + 1;
        r = (r + ans) % n + 1;
        if (l > r) swap(l, r);
        l--, r--;

        ans = query(rt[r + 1], 0, n - 1, l);
        cout << ans << '\n';
    }

    return 0;
}