[CF813E] Army Creation(分块,二分)

题目链接:http://codeforces.com/contest/813/problem/E

题意:n个人,每一个人有一个id。现在排成一排,现在要在n个人中的[l,r]区间内挑选,使得每一个id的出现次数不超过k。问每一个区间最多能选多少人。强制在线。

学习了一个问题转化的巧妙思路,考虑每一个人不被统计的条件:这个人之前已经出现了k个人,并且这k个人出现在查询的区间内了。

统计每一种id出现的位置i,每次更新与位置i出现的人id相同的前k+1个人的位置,假如没有则置-1,认为这个人没被删。

按块更新,在块内排序。查询的时候就相当于查在某块内小于左端点值的数的个数。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef pair<int, int> pii;
 5 const int maxn = 100100;
 6 int n, k, q, sz;
 7 int a[maxn], pos[maxn], be[maxn];
 8 vector<int> b[maxn], G[maxn];
 9 
10 int query(int l, int r) {
11     int ret = 0;
12     for(int i = l; i <= r; ) {
13         if(i % sz == 0 && i + sz <= r) {
14             ret += lower_bound(b[be[i]].begin(), b[be[i]].end(), l) - b[be[i]].begin();
15             i += sz;
16         }
17         else if(pos[i++] < l) ret++;
18     }
19     return ret;
20 }
21 
22 signed main() {
23     // freopen("in", "r", stdin);
24     int l, r;
25     while(~scanf("%d%d",&n,&k)) {
26         sz = sqrt(n);
27         memset(pos, -1, sizeof(pos));
28         for(int i = 0; i < n; i++) b[i].clear(), G[i].clear();
29         for(int i = 0; i < n; i++) {
30             scanf("%d", &a[i]);
31             be[i] = i / sz;
32             G[a[i]].push_back(i);
33             if(G[a[i]].size() >= k + 1) pos[i] = G[a[i]][G[a[i]].size()-k-1];
34             b[be[i]].push_back(pos[i]);
35         }
36         for(int i = 0; i <= n / sz; i++) sort(b[i].begin(), b[i].end());
37         scanf("%d", &q);
38         int ret = 0;
39         while(q--) {
40             scanf("%d%d",&l,&r);
41             l = (l + ret) % n + 1;
42             r = (r + ret) % n + 1;
43             if(l > r) swap(l, r);
44             printf("%d\n", ret=query(--l, --r));
45         }
46     }
47     return 0;
48 }

 

posted @ 2017-06-18 15:19  Kirai  阅读(161)  评论(0编辑  收藏  举报