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