题意:给你一个数组a,有m次询问,每次问区间[l, r]中最多可以取多少个数字(相同的数字最多取k个),强制在线。
思路:可以先预处理一个数组b,b[i]指和a[i]相同的从i开始第k + 1个数的位置。求出b数组后,如果询问区间[l, r]中的数,若b[i]大于r,说明从这个数往后的和b[i]相同的数的个数小于等于k,说明此数可取。那么问题转化为了询问[l, r]中b[i] > r的数的个数。因为只需要单点修改和区间询问,我们可以很容易想到用树套树主席树来解决这个问题。
代码:
#include <bits/stdc++.h> using namespace std; const int maxn = 100010; struct SegementTree { int lson, rson; int val; }; SegementTree tr[maxn * 200]; int root[maxn], tot; void pushup(int x) { tr[x].val = tr[tr[x].lson].val + tr[tr[x].rson].val; } void insert(int lnow, int rnow, int l, int r, int pos) { tr[rnow] = tr[lnow]; if(l == r) { tr[rnow].val++; return; } int mid = (l + r) >> 1; if(pos <= mid) { tr[rnow].lson = ++tot; insert(tr[lnow].lson, tot, l, mid, pos); } else { tr[rnow].rson = ++tot; insert(tr[lnow].rson, tot, mid + 1, r, pos); } pushup(rnow); } int query(int lnow, int rnow, int l, int r, int ql, int qr) { if(l >= ql && r <= qr) { return tr[rnow].val - tr[lnow].val; } int mid = (l + r) >> 1, ans = 0; if(ql <= mid) ans += query(tr[lnow].lson, tr[rnow].lson, l, mid, ql, qr); if(qr > mid) ans += query(tr[lnow].rson, tr[rnow].rson, mid + 1, r, ql, qr); return ans; } int a[maxn], b[maxn]; vector<int> c[maxn]; int main() { int n, m, k, x, y; scanf("%d%d", &n, &k); for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); } for (int i = n; i >= 1; i--) { if(c[a[i]].size() < k) b[i] = n + 1; else b[i] = c[a[i]][c[a[i]].size() - k]; c[a[i]].push_back(i); } for (int i = 0; i <= n; i++) root[i] = ++tot; for (int i = 1; i <= n; i++) insert(root[i - 1], root[i], 1, n + 1, b[i]); scanf("%d", &m); int ans = 0; while(m--) { scanf("%d%d", &x, &y); x = ((x + ans) % n) + 1; y = ((y + ans) % n) + 1; if(x > y) swap(x, y); ans = query(root[x - 1], root[y], 1, n + 1, y + 1, n + 1); printf("%d\n", ans); } }
原文地址:https://www.cnblogs.com/pkgunboat/p/10599335.html
时间: 2024-11-02 23:54:16