Codeforces 813E 主席树

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

  

猜你喜欢

转载自www.cnblogs.com/pkgunboat/p/10599335.html