BZOJ4939[Ynoi2016]掉进兔子洞(莫队+bitset)

题目链接

BZOJ

洛谷

扯点别的

听说这是比较亲民的一道Ynoi,于是我就去摸了一下,于是一个晚上就没了……不过至少还是学到了\(bitset\)维护可重集合的方法,以及当空间开不下时分组处理询问的花操作……

解析

大佬们说容易想到bitset,那就想到bitset吧……

这种形如“几个集合共同含有的元素”的问题容易让人想\(bitset\)搞一搞,但是我们发现裸的\(bitset\)只能表示有没有,不能表示有多少,下面就是神奇的让\(bitset\)能表示有多少的方法

就这道题而言首先肯定是离散化,但是离散化时我们不去重,这样相同元素会是连续的一段,我们取第一个出现的位置作为离散化后的值,简单举个例子:1,4,2,4,4,5 => hash[1] = 0,hash[2] = 1,hash[4] = 2,hash[5] = 5

假设我们要加入一个数\(x\),它之前已经在这个集合中出现了\(cnt\)次,那么我们把\(x + cnt\)那一位置为\(1\),删除同理,即:bitset中第\(i + j\)个位置表示数字\(i\)\(j + 1\)次出现

这样当我们把每个\(bitset\)与起来就相当于对每个数的出现次数取了个\(min\)

回到题目,假设我们知道了三个区间的\(bitset\)与起来是\(S\),那么答案显然就是\(len1 + len2 + len3 - |S| \times 3\)

我们发现\(bitset\)加入和删除都能很快处理,询问的又是区间,那么就可以上莫队试试了,莫队的时候维护一下每个数出现的次数即可

还有一点,直接开\(1e5\)\(bitset\)肯定开不下,于是我们把询问分组,每组上一次莫队即可

分组大小可以算算,我比较懒就从\(1e4\)开始试然后到\(3e4\)的时候就过了……

代码

#include <cstdio>
#include <cstring>
#include <iostream>
#include <bitset>
#include <algorithm>
#define MAXN 100003
#define belong(x) ((x) / per_block)

typedef long long LL;
struct Query {
	int l, r, id;
} qry[90005];
int arr[MAXN], hash[MAXN], cnt[MAXN], N, M, per_block, idx;
std::bitset<MAXN> cur, ans[30003];
bool vis[30003];

bool operator <(const Query &q1, const Query &q2) {
	return belong(q1.l) == belong(q2.l) ? q1.r < q2.r : belong(q1.l) < belong(q2.l);
}
bool cmp(const Query &q1, const Query &q2) { return q1.id < q2.id; }

int main() {
	scanf("%d%d", &N, &M);
	for (int i = 0; i < N; ++i) scanf("%d", arr + i), hash[i] = arr[i];
	std::sort(hash, hash + N);
	for (int i = 0; i < N; ++i)
		arr[i] = std::lower_bound(hash, hash + N, arr[i]) - hash;
	while (per_block * per_block < N) ++per_block;

	while (M) {
		memset(cnt, 0, sizeof cnt);
		memset(vis, 0, sizeof vis);
		cur.reset();
		int tot = std::min(30000, M);
		for (int k = 0; k < tot * 3; ++k) scanf("%d%d", &qry[k].l, &qry[k].r), qry[k].id = k / 3;
		std::sort(qry, qry + tot * 3);
		for (int l = 0, r = -1, i = 0; i < tot * 3; ++i) {
			--qry[i].l, --qry[i].r;
			while (r < qry[i].r) ++r, cur[arr[r] + cnt[arr[r]]] = 1, ++cnt[arr[r]];
			while (r > qry[i].r) --cnt[arr[r]], cur[arr[r] + cnt[arr[r]]] = 0, --r;
			while (l < qry[i].l) --cnt[arr[l]], cur[arr[l] + cnt[arr[l]]] = 0, ++l;
			while (l > qry[i].l) --l, cur[arr[l] + cnt[arr[l]]] = 1, ++cnt[arr[l]];
			if (!vis[qry[i].id]) vis[qry[i].id] = 1, ans[qry[i].id] = cur;
			else ans[qry[i].id] &= cur;
		}
		std::sort(qry, qry + tot * 3, cmp);
		for (int i = 0; i < tot; ++i) {
			int p1 = i * 3, p2 = p1 + 1, p3 = p2 + 1;
			printf("%d\n", qry[p1].r - qry[p1].l + qry[p2].r - qry[p2].l + qry[p3].r - qry[p3].l + 3 - (int)ans[i].count() * 3);
		}
		M -= tot;
	}

	return 0;
}
//Rhein_E
posted @ 2019-03-10 20:36  Rhein_E  阅读(135)  评论(0编辑  收藏  举报