「BZOJ 2821 && Luogu P4135」作诗

给出一个长为 n 的数列,以及 m 个操作,操作涉及询问区间内多少个数出现正偶数次。 \((1\le n,m \le 10^5)\)

Luogu

分析

这题和分块的求区间众数题类似,于是同样用分块来做。

假设我们已经求出每个块之间的答案,考虑求区间 [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;
}
posted @ 2020-01-15 11:12  小蒟蒻hlw  阅读(105)  评论(0)    收藏  举报