BZOJ 2821作诗(Poetize) 分块
Description
有一个长度为n的序列,序列每个元素的范围[1,c],有m个询问x y,表示区间[x,y]中出现正偶数次的数的种类数。
Solution
大力分块解决问题。
把序列分块,f[i][j]表示第i块到第j块的答案,并记录块的前缀数的出现次数。
f[i][j]直接暴力算,块的前缀数的出现次数也可以直接算,都是nsqrt(n)。
遇到询问x y,中间答案的块可以直接统计,然后再暴力统计左右两边零碎的贡献,也是nsqrt(n)。
Code
1 #include <cstdio> 2 #include <cstdlib> 3 #include <cstring> 4 #include <string> 5 #include <algorithm> 6 #include <cmath> 7 8 using namespace std; 9 10 #define REP(i, a, b) for (int i = (a), i##_end_ = (b); i <= i##_end_; ++i) 11 const int maxn = 1e5+10; 12 int n, c, m, a[maxn]; 13 int bel[maxn], l[maxn], r[maxn]; 14 int cnt[350][maxn], s_block, t[maxn], f[350][350]; 15 16 int solve(int x, int y) 17 { 18 int L = bel[x], R = bel[y]; 19 if (L == R) 20 { 21 REP(i, x, y) t[a[i]] = 0; 22 int ret = 0; 23 REP(i, x, y) if (t[a[i]] ++) ret += (t[a[i]]&1) ? -1 : 1; 24 return ret; 25 } 26 else 27 { 28 int ret = f[L+1][R-1]; 29 REP(i, x, r[L]) t[a[i]] = 0; 30 REP(i, l[R], y) t[a[i]] = 0; 31 REP(i, x, r[L]) if (!t[a[i]]) t[a[i]] = cnt[R-1][a[i]]-cnt[L][a[i]]; 32 REP(i, l[R], y) if (!t[a[i]]) t[a[i]] = cnt[R-1][a[i]]-cnt[L][a[i]]; 33 REP(i, x, r[L]) if (t[a[i]] ++) ret += (t[a[i]]&1) ? -1 : 1; 34 REP(i, l[R], y) if (t[a[i]] ++) ret += (t[a[i]]&1) ? -1 : 1; 35 return ret; 36 } 37 } 38 39 int main() 40 { 41 scanf("%d %d %d", &n, &c, &m); 42 REP(i, 1, n) scanf("%d", &a[i]); 43 int block = int(sqrt(n)); 44 REP(i, 1, n) 45 { 46 bel[i] = i/block+1, r[bel[i]] = i; 47 if (i == 1 || bel[i] != bel[i-1]) l[bel[i]] = i; 48 } 49 s_block = n/block+1; 50 REP(i, 1, s_block) 51 { 52 REP(j, 1, c) t[j] = 0; 53 REP(j, i, s_block) 54 { 55 f[i][j] = (j == i) ? 0 : f[i][j-1]; 56 REP(k, l[j], r[j]) if (t[a[k]] ++) f[i][j] += (t[a[k]]&1) ? -1 : 1; 57 } 58 } 59 REP(i, 1, s_block) 60 { 61 REP(j, 1, c) cnt[i][j] = cnt[i-1][j]; 62 REP(j, l[i], r[i]) cnt[i][a[j]] ++; 63 } 64 int x, y, ans = 0; 65 while (m --) 66 { 67 scanf("%d %d", &x, &y); 68 x = (x+ans)%n+1, y = (y+ans)%n+1; 69 if (x > y) swap(x, y); 70 printf("%d\n", ans = solve(x, y)); 71 } 72 return 0; 73 }
Nothing is impossible!