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
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
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
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;
}