Solution -「洛谷 P4688」「YunoOI 2016」掉进兔子洞

Description

(Link)[https://www.luogu.com.cn/problem/P4688].

每次询问三个区间,把三个区间中同时出现的数一个一个删掉,问最后三个区间剩下的数的个数和,询问独立。

Solution

读完题,我们可以发现每轮询问的答案是这个东西:

\[\sum_{i=1}^{3}(r_{i}-l_{i}+1)-P\times 3 \]

其中,\(P\) 表示三个区间里面的公共颜色数量。

前面那个 \(\sum\) 里面的东西很好维护,我们主要来讲后面一部分的维护方法。

首先初始序列的 \(a_{i}\) 是一定要离散化一遍的。

那么我们如何表示出出现的最少次数呢?

如果这是一个二进制串的话,我们可以发现这就是一个 \(\operatorname{bit\_and}\) 的操作。

对于每个询问,我们可以把三个区间分开处理再合并。直接维护复杂度过高,用 bitset 优化。由于这是个二进制序列,所以离散化的时候不能去重,否则答案就会偏大。

直接操作容易 MLE,这里介绍一个小trick。我们可以把询问分批处理,这样就行了。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#include <bitset>
#include <cmath>

using namespace std;
typedef long long LL;
typedef unsigned long long ULL;

const int N = 1e5 + 5;
const int F = 23333 + 5;
int n, m, block, origin[N], appear[N];
int cnt[N], ans[N], vis[25005];
int l1[N], l2[N], l3[N];
int r1[N], r2[N], r3[N];
bitset < N > S[F];
vector < int > disc;
struct AskSet {
	int l, r;
	int id, p;
} Q[N];

int GetID(int x) {
	return lower_bound(disc.begin(), disc.end(), x) - disc.begin() + 1;
}

bool SortWith(const AskSet& x, const AskSet& y) {
	return (x.p < y.p) || (x.p == y.p && x.r < y.r);
}

void MakeCont(int pos, int flags, bitset < N >& bit) {
	pos = origin[pos];
	if (flags > 0) bit[pos + cnt[pos]] = 1;
	else bit[pos + cnt[pos] - 1] = 0;
	cnt[pos] += flags;
}

void Contribute(int fr, int ba) {
	memset(cnt, 0, sizeof cnt);
	memset(vis, 0, sizeof vis);
	bitset < N > bit; bit.reset();
	int l = 1, r = 0, subs = 0;
	for (int i = fr; i <= ba; ++i) {
		Q[++subs] = {l1[i], r1[i], i, appear[l1[i]]};
		Q[++subs] = {l2[i], r2[i], i, appear[l2[i]]};
		Q[++subs] = {l3[i], r3[i], i, appear[l3[i]]};
		ans[i] += (r3[i] - l3[i]) + (r2[i] - l2[i]) + (r1[i] - l1[i]) + 3;
	}
	sort(Q + 1, Q + 1 + subs, SortWith);
	for (int i = 1; i <= subs; ++i) {
		while (r < Q[i].r) MakeCont(++r, 1, bit);
		while (l > Q[i].l) MakeCont(--l, 1, bit);
		while (r > Q[i].r) MakeCont(r--, -1, bit);
		while (l < Q[i].l) MakeCont(l++, -1, bit);
		if (!vis[Q[i].id - fr + 1]) {
			vis[Q[i].id - fr + 1] = 1;
			S[Q[i].id - fr + 1] = bit;
		} else {
			S[Q[i].id - fr + 1] &= bit;
		}
	}
	for (int i = fr; i <= ba; ++i)
		ans[i] -= S[i - fr + 1].count() * 3;
}

signed main() {
	scanf("%d %d", &n, &m);
	block = sqrt(n);
	for (int i = 1; i <= n; ++i) {
		scanf("%d", &origin[i]);
		appear[i] = (i - 1) / block + 1;
		disc.push_back(origin[i]);
	}
	sort(disc.begin(), disc.end());
	for (int i = 1; i <= n; ++i)
		origin[i] = GetID(origin[i]);
	for (int i = 1; i <= m; ++i) {
		scanf("%d %d", &l1[i], &r1[i]);
		scanf("%d %d", &l2[i], &r2[i]);
		scanf("%d %d", &l3[i], &r3[i]);
	}
	for (int i = 1; i <= m; i += 23333) Contribute(i, min(m, i + 23332));
	for (int i = 1; i <= m; ++i) printf("%d\n", ans[i]);
	return 0;
}
posted @ 2020-10-12 09:31  cirnovsky  阅读(63)  评论(0编辑  收藏  举报