【BZOJ 2821】作诗
【题目链接】
https://www.lydsy.com/JudgeOnline/problem.php?id=2821
【算法】
如果不强制在线,显然莫队是可以解决此题的,那么,强制在线怎么办呢? 分块
将这个序列分成sqrt(n)段(sqrt表示开方),预处理每段每个数出现的次数与该段“多少数出现了正偶数次”,就可以在线回答询问了
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 100005; const int MAXB = 320; int i,n,m,c,l,r,x,y,len,block,lastans; int a[MAXN],belong[MAXN],cnt[MAXN],L[MAXB],R[MAXB]; int sum[MAXB][MAXN],ans[MAXB][MAXB]; inline void init() { int i,j,k; len = (int)sqrt(n); block = n / len; for (i = 1; i <= block; i++) { L[i] = (i - 1) * len + 1; R[i] = i * len; } if (R[block] < n) { block++; L[block] = R[block-1] + 1; R[block] = n; } for (i = 1; i <= n; i++) belong[i] = (i - 1) / len + 1; for (i = 1; i <= n; i++) sum[belong[i]][a[i]]++; for (i = 1; i <= block; i++) { for (j = 1; j <= c; j++) { sum[i][j] += sum[i-1][j]; } } for (i = 1; i <= block; i++) { for (j = i; j <= block; j++) { ans[i][j] = ans[i][j-1]; for (k = L[j]; k <= R[j]; k++) { cnt[a[k]]++; if (cnt[a[k]] % 2 == 0) ans[i][j]++; else if (cnt[a[k]] > 1) ans[i][j]--; } } for (j = L[i]; j <= n; j++) cnt[a[j]]--; } } inline int query(int l,int r) { int i,ret = 0,t; int p = belong[l], q = belong[r]; if (p == q) { for (i = l; i <= r; i++) { cnt[a[i]]++; if (cnt[a[i]] % 2 == 0) ret++; else if (cnt[a[i]] > 1) ret--; } for (i = l; i <= r; i++) cnt[a[i]]--; return ret; } else { ret = ans[p+1][q-1]; for (i = l; i <= R[p]; i++) { cnt[a[i]]++; t = sum[q-1][a[i]] - sum[p][a[i]] + cnt[a[i]]; if (t % 2 == 0) ret++; else if (t > 1) ret--; } for (i = L[q]; i <= r; i++) { cnt[a[i]]++; t = sum[q-1][a[i]] - sum[p][a[i]] + cnt[a[i]]; if (t % 2 == 0) ret++; else if (t > 1) ret--; } for (i = l; i <= R[p]; i++) cnt[a[i]]--; for (i = L[q]; i <= r; i++) cnt[a[i]]--; return ret; } } int main() { scanf("%d%d%d",&n,&c,&m); for (i = 1; i <= n; i++) scanf("%d",&a[i]); init(); lastans = 0; for (i = 1; i <= m; i++) { scanf("%d%d",&x,&y); l = (x + lastans) % n + 1; r = (y + lastans) % n + 1; if (l > r) swap(l,r); printf("%d\n",lastans = query(l,r)); } return 0; }