[SNOI2017]一个简单的询问(莫队+容斥)

题目描述

给你一个长度为 \(N\) 的序列 \(a_i\)a,\(1\leq i\leq N\),和 \(q\) 组询问,每组询问读入 \(l_1,r_1,l_2,r_2\),需输出\(\sum\limits_{x=0}^\infty \text{get}(l_1,r_1,x)\times \text{get}(l_2,r_2,x)\) 表示计算区间 \([l,r]\) 中,数字 \(x\) 出现了多少次。

Analysis

可以离线每次都给你询问一个区间我们很容易想到这是一道莫队题。但这题的询问是两个区间,我们想办法把其拆成一个区间。

通过容斥,可以得到答案即为 \((get(1,r_1,x)-get(1,l_1)) \times (get(1,r_2,x)-get(1,l_2,x))\),拆开后我们发现我们只要解决 \(get(1,l,x) \times get(1,r,x)\) 的问题。而我们发现这里其实只有两个变量 \(l,r\)。可是这跟普通的莫队又不太一样,其实只需改一下扩展即可,也就是l减小即删除,增大即增加,r也一样,然后就做完了。

所以如果这种多个区间的问题以后我们不妨考虑一下化成一个区间。

记住莫队其实只是排序的过程,而扩展不是不变的,根据不同的问题会有不同的扩展形式,也可能有不同的扩展方法。

代码的话也非常简短:

#include <bits/stdc++.h>
using namespace std;
const int N = 50010;
template <typename T> void read(T &x) {
	T f = 1;
	char ch = getchar();
	for (; '0' > ch || ch > '9'; ch = getchar()) if (ch == '-') f = -1;
	for (x = 0; '0' <= ch && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
	x *= f;
}
int n, m;
int a[N];
int cnt[2][N];
int len, bl[N];
struct node{
	int l, r, f, id;
	friend bool operator < (node x, node y) {
		if (bl[x.l] != bl[y.l]) return x.l < y.l;
		if (bl[x.l] % 2) return x.r < y.r;
		return x.r > y.r; 
	}
}q[N << 2];
int ans[N];
int top;
int nowl = 0, nowr = 0, nowans = 0;
void add(int id, int c) {
	nowans -= cnt[0][c] * cnt[1][c];
	cnt[id][c]++;
	nowans += cnt[0][c] * cnt[1][c];
}
void del(int id, int c) {
	nowans -= cnt[0][c] * cnt[1][c];
	cnt[id][c]--;
	nowans += cnt[0][c] * cnt[1][c];
}
void Push(int l, int r, int f, int id) {
	if (l > r) swap(l, r);
	q[++top] = node{l, r, f, id};
}
int main() {
	read(n);
	for (int i = 1; i <= n; i++) read(a[i]);
	len = sqrt(n);
	for (int i = 1; i <= n; i++) bl[i] = (i - 1) / len + 1;
	read(m);
	for (int i = 1, l1, r1, l2, r2; i <= m; i++) {
		read(l1); read(r1); read(l2); read(r2);
		l1--, l2--;
		if (l1 == 0 && l2 == 0) {
			Push(r1, r2, 1, i);
		} else if (l1 == 0) {
			Push(r1, r2, 1, i);
			Push(r1, l2, -1, i);
		} else if (l2 == 0) {
			Push(r1, r2, 1, i);
			Push(l1, r2, -1, i);
		} else {
			Push(r1, r2, 1, i);
			Push(r1, l2, -1, i);
			Push(l1, r2, -1, i);
			Push(l1, l2, 1, i);
		}
	}
	sort(q + 1, q + 1 + top);
	for (int i = 1; i <= top; i++) {
		while (nowl < q[i].l) add(0, a[++nowl]);
		while (nowr < q[i].r) add(1, a[++nowr]);
		while (nowl > q[i].l) del(0, a[nowl--]);
		while (nowr > q[i].r) del(1, a[nowr--]);
		ans[q[i].id] += nowans * q[i].f;
	}
	for (int i = 1; i <= m; i++) {
		printf("%d\n", ans[i]);
	}
	return 0;
}

猜你喜欢

转载自www.cnblogs.com/zcr-blog/p/13394550.html