题意:给你一个数组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);
}
}