「BZOJ 2821 && Luogu P4135」作诗
给出一个长为 n 的数列,以及 m 个操作,操作涉及询问区间内多少个数出现正偶数次。 \((1\le n,m \le 10^5)\)
分析
这题和分块的求区间众数题类似,于是同样用分块来做。
假设我们已经求出每个块之间的答案,考虑求区间 [l, r] 的答案。显然,如果 l 和 r 在同一个块内,直接暴力扫一遍,同时用一个桶统计数量,变为偶数则 ans++ ,变为奇数则 ans-- 。如果不在同一个块内,设 f[i][j] 为求出的第 i 块到第 j 块的答案,则直接令 ans = f[bl[l]+1][bl[r]-1] ,同样地,在左边的不完整块暴力扫一遍并统计,如果块内这个数值出现的次数为偶数,说明已经将这个数统计入答案中了,所以外面的数量为奇数的话,答案需要减去 1 ;而如果块内的次数为奇数,则当块外数量同为奇数时,答案需要加 1 ,同样还需要判断块内没有出现这个数的情况。
所以我们需要预处理出 f[i][j] ,并能 \(O(1)\) 求出两个块之间某个数出现的次数。 \(O(n^2)\) 显然是不行的,但我们可以 \(O(n \sqrt{n})\) 预处理,只能将数组扫一遍,并统计块与块间的答案。
如下:
for (register int i = 1; i <= n; ++i) {
read(a[i]);
bl[i] = (i - 1) / blo + 1;
for (register int j = 1; j <= bl[i]; ++j) {
if (i % blo == 1) f[j][bl[i]] += f[j][bl[i]-1];
if (cnt[j][a[i]] & 1) f[j][bl[i]]++;
else if (cnt[j][a[i]]) f[j][bl[i]]--;
cnt[j][a[i]]++; //cnt[i][j]表示第i块到最后一块,数值j出现的次数
}
}
代码
跑的很慢......
Luogu 上加了卡常,开了 o2 才过......
//=========================
// author:hliwen
// date:2020.1.15
//=========================
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 100005
#define il inline
#define re register
#define tie0 cin.tie(0),cout.tie(0)
#define fastio ios::sync_with_stdio(false)
#define File(x) freopen(x".in","r",stdin);freopen(x".out","w",stdout)
using namespace std;
typedef long long ll;
template <typename T> inline void read(T &x) {
T f = 1; x = 0; char c;
for (c = getchar(); !isdigit(c); c = getchar()) if (c == '-') f = -1;
for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
x *= f;
}
int n, c, m, blo, ans;
int a[N], bl[N], cnt[351][N], f[351][351], tot[N];
inline int calc(int l, int r, int x) {
return cnt[l][x] - cnt[r+1][x];
}
inline int query(int l, int r) {
register int ret = f[bl[l]+1][bl[r]-1];
memset(tot, 0, sizeof tot);
if (bl[l] != bl[r]) {
for (register int i = l; i <= bl[l] * blo; ++i) {
if (calc(bl[l] + 1, bl[r] - 1, a[i]) & 1) {
if (tot[a[i]] & 1) ret--;
else ret++;
}
else if (calc(bl[l] + 1, bl[r] - 1, a[i])) {
if (tot[a[i]] & 1) ret++;
else ret--;
}
else {
if (tot[a[i]] & 1) ret++;
else if (tot[a[i]]) ret--;
}
tot[a[i]]++;
}
for (register int i = (bl[r] - 1) * blo + 1; i <= r; ++i) {
if (calc(bl[l] + 1, bl[r] - 1, a[i]) & 1) {
if (tot[a[i]] & 1) ret--;
else ret++;
}
else if (calc(bl[l] + 1, bl[r] - 1, a[i])) {
if (tot[a[i]] & 1) ret++;
else ret--;
}
else {
if (tot[a[i]] & 1) ret++;
else if (tot[a[i]]) ret--;
}
tot[a[i]]++;
}
}
else {
ret = 0;
for (register int i = l; i <= r; ++i) {
if (tot[a[i]] & 1) ret++;
else if (tot[a[i]]) ret--;
tot[a[i]]++;
}
}
return ret;
}
int main() {
register int l, r;
read(n), read(c), read(m);
blo = sqrt(n);
for (register int i = 1; i <= n; ++i) {
read(a[i]);
bl[i] = (i - 1) / blo + 1;
for (register int j = 1; j <= bl[i]; ++j) {
if (i % blo == 1) f[j][bl[i]] += f[j][bl[i]-1];
if (cnt[j][a[i]] & 1) f[j][bl[i]]++;
else if (cnt[j][a[i]]) f[j][bl[i]]--;
cnt[j][a[i]]++;
}
}
for (register int i = 1; i <= m; ++i) {
read(l), read(r);
l = (l + ans) % n + 1, r = (r + ans) % n + 1;
if (l > r) swap(l, r);
printf("%d\n", ans = query(l, r));
}
return 0;
}

浙公网安备 33010602011771号