这道题一眼看去好像不能用什么数据结构做出,所以就只有打分块暴力了,刚开始想的时候没有想到用前缀和,所以写出来的时间复杂度就达到了O(nnlogn),交上去不仅T了还WA了。后面加上了前缀和时间复杂度就可以过了
思路
假设将整个序列分为T块,那么考虑预处理一个数组timi,j表示前i个块中,字符j出现的次数,这样就可以快速求出每一段块中各数字出现的次数了,然后再预处理一个数组numi,j表示第i个块到第j个块中出现了正偶数次的数字的个数,那么对于查询的区间[L,R],分为两种情况
[L,R]在同一个块中,这种情况就直接暴力枚举每个数字即可
[L,R]不在同一个块中,考虑分为三段[L,r),[l,r],(r,R],其中[l,r]为完整的块,那么只需要在枚举的时候判断一下加上当前这个字符对答案是否有影响,详细解释见代码
#include<iostream> #include<cstdio> #include<cmath> #include<cstring> #define get(i) ((i - 1) / len + 1) using namespace std; const int N = 1e5 + 5;
int len; int tim[405][N], num[405][405], a[N], temp[N];
int query(int l, int r) { int res = 0; if (get(l) == get(r)) { for (int i = l; i <= r; i++) temp[a[i]]++; for (int i = l; i <= r; i++) { if (temp[a[i]] > 0 && temp[a[i]] % 2 == 0)//注意要大于0 res++; temp[a[i]] = 0; } }//暴力枚举的情况 else { int i = l, j = r; res += num[get(i) + 1][get(j) - 1]; while (get(i) == get(l)) { temp[a[i]]++; if ((temp[a[i]] + tim[get(j) - 1][a[i]] - tim[get(i)][a[i]]) > 0 && (temp[a[i]] + tim[get(j) - 1][a[i]] - tim[get(i)][a[i]]) % 2 == 0) res++; else if ((temp[a[i]] + tim[get(j) - 1][a[i]] - tim[get(i)][a[i]]) > 1 && (temp[a[i]] + tim[get(j) - 1][a[i]] - tim[get(i)][a[i]]) % 2)//大于1是为了防止多减 res--; i++; } while (get(j) == get(r)) { temp[a[j]]++; if ((temp[a[j]] + tim[get(j) - 1][a[j]] - tim[get(i) - 1][a[j]]) > 0 && (temp[a[j]] + tim[get(j) - 1][a[j]] - tim[get(i) - 1][a[j]]) % 2 == 0) res++; else if ((temp[a[j]] + tim[get(j) - 1][a[j]] - tim[get(i) - 1][a[j]]) > 1 && (temp[a[j]] + tim[get(j) - 1][a[j]] - tim[get(i) - 1][a[j]]) % 2) res--; j--; } for (int k = l; k < i; k++) temp[a[k]] = 0; for (int k = j + 1; k <= r; k++) temp[a[k]] = 0;//清空数组,因为用memset会超时 } return res; }
int main() { int n, c, m; scanf("%d%d%d", &n, &c, &m); len = sqrt(n); for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); tim[get(i)][a[i]]++; } for (int i = 1; i <= get(n); i++) { for (int j = 0; j <= c; j++) tim[i][j] += tim[i - 1][j]; }//预处理前缀和 for (int i = 1; i <= get(n); i++) { memset(temp, 0, sizeof(temp)); for (int j = i; j <= get(n); j++) { int r = j * len; num[i][j] = num[i][j - 1]; for (int k = (j - 1) * len + 1; k <= r; k++) { temp[a[k]]++; if (temp[a[k]] > 0 && temp[a[k]] % 2 == 0) num[i][j]++; else if (temp[a[k]] > 1 && temp[a[k]]) num[i][j]--; } } }//预处理每个完整的块中的答案 memset(temp, 0, sizeof(temp)); int ans = 0; while (m--) { int l, r; scanf("%d%d", &l, &r); l = (l + ans) % n + 1, r = (r + ans) % n + 1; if (l > r) swap(l, r); printf("%d\n", ans = query(l, r));//查询 } }
|