Educational Codeforces Round 22 E. Army Creation(主席树)
As you might remember from our previous rounds, Vova really likes computer games. Now he is playing a strategy game known as Rage of Empires.
In the game Vova can hire n different warriors; ith warrior has the type a**i. Vova wants to create a balanced army hiring some subset of warriors. An army is called balanced if for each type of warrior present in the game there are not more than k warriors of this type in the army. Of course, Vova wants his army to be as large as possible.
To make things more complicated, Vova has to consider q different plans of creating his army. ith plan allows him to hire only warriors whose numbers are not less than l**i and not greater than r**i.
Help Vova to determine the largest size of a balanced army for each plan.
Be aware that the plans are given in a modified way. See input section for details.
Input
The first line contains two integers n and k (1 ≤ n, k ≤ 100000).
The second line contains n integers a1, a2, ... a**n (1 ≤ a**i ≤ 100000).
The third line contains one integer q (1 ≤ q ≤ 100000).
Then q lines follow. ith line contains two numbers x**i and y**i which represent ith plan (1 ≤ x**i, y**i ≤ n).
You have to keep track of the answer to the last plan (let's call it last). In the beginning last = 0. Then to restore values of l**i and r**i for the ith plan, you have to do the following:
- l**i = ((x**i + last) mod n) + 1;
- r**i = ((y**i + last) mod n) + 1;
- If l**i > r**i, swap l**i and r**i.
Output
Print q numbers. ith number must be equal to the maximum size of a balanced army when considering ith plan.
Example
input
Copy
6 2
1 1 1 2 2 2
5
1 6
4 3
1 1
2 6
2 6
output
Copy
2
4
1
3
2
题意大概是给定一个数组,每次询问给一个区间[l, r],对于这个区间的每一类数,如果数量大于k则以k计,数量小于k则正常计,问区间内有多少数。要求强制在线。
对于这个题最关键的一点是,如果区间内一个位置pos对于答案有贡献,设这个位置的数是x,当且仅当所有值为x的位置中在pos前面k个的这个位置比l小。举例来说,设数组为[3, 3, 3, 3],k = 2,询问区间为[1, 3],则第三个3前面k个的位置为1,但是1这个位置不比l小,因此第三个3对于答案没有贡献(仅前两个3有贡献)。
因此,可以预处理出数组b,b[i]代表的是i这个位置的数所在的所有位置中,靠前的距离i这个位置为k的那个位置。例如对于样例[1, 1, 1, 2, 2, 2],对应的b数组为[0, 0, 1, 0, 0, 4](如果前面没有k个,直接设置为最小的位置0)。这样问题就转化为快速统计[l, r]中b数组有多少个数小于l。这可以用主席树来维护。update部分不用更改,仅有每次询问的部分需要修改一下模版。详情见代码:
#include <iostream>
#include <vector>
#include <cstring>
#define mid (l+r)/2
#define N 100005
#define LOG 20
using namespace std;
int n, k, a[100005], b[100005], q, tot = 0;
vector<int> p[100005];
int T[N], sum[N*LOG], L[N*LOG], R[N*LOG];
inline int build(int l, int r) {
int rt = ++tot;
if (l < r){
L[rt] = build(l, mid);
R[rt] = build(mid+1, r);
}
return rt;
}
inline int update(int pre, int l, int r, int x) {
int rt = ++tot;
L[rt] = L[pre]; R[rt] = R[pre]; sum[rt] = sum[pre] + 1;
if (l < r){
if (x <= mid) L[rt] = update(L[pre], l, mid, x);
else R[rt] = update(R[pre], mid + 1, r, x);
}
return rt;
}
inline int query(int u, int v, int l, int r, int k) {
if (r <= k) return sum[v] - sum[u];
if(l > k) return 0;
int ans = 0;
if (k <= mid) ans = query(L[u], L[v], l, mid, k);
else ans = query(L[u], L[v], l, mid, k) + query(R[u], R[v], mid + 1, r, k);
return ans;
}
int main() {
memset(T, 0, sizeof T); memset(sum, 0, sizeof sum);
memset(L, 0, sizeof L); memset(R, 0, sizeof R);
cin >> n >> k;
for(int i = 1; i <= n; i++) {
cin >> a[i];
p[a[i]].push_back(i);
if(p[a[i]].size() > k) {
b[i] = p[a[i]][p[a[i]].size() - 1 - k];
} else {
b[i] = 0;
}
}
T[0] = build(0, 100000);
for(int i = 1; i <= n; i++) {
T[i] = update(T[i - 1], 0, 100000, b[i]);
}
cin >> q;
int lst = 0;
while(q--) {
int x, y, l, r;
cin >> x >> y;
l = (x + lst) % n + 1, r = (y + lst) % n + 1;
if(l > r) swap(l, r);
int ans = query(T[l - 1], T[r], 0, 100000, l - 1);
// 快速统计[l, r]中b数组有多少个数小于l
cout << ans << endl;
lst = ans;
}
return 0;
}