Loading

莫队二次离线

P4887 【模板】莫队二次离线(第十四分块(前体)) - 洛谷

给一个序列 \(a\) ,每次给一个查询区间 \([l,r]\)

查询 \(l \le i < j \le r\)\(a_i\) 异或 \(a_j\) 正好有 \(k\) 个二进制 \(bit\) 的个数

还是用莫队的思想,在挪的时候更新答案的方式如图

现在我们要解决的问题是如何求 \(A_i\) 与区间 \([L,R]\) 的配对数。

这里我们利用前缀和的思想,令 \(S_R\) 表示区间 \([1,R]\)\(A_i\) 的配对数

则要求的就是 \(S_R - S_{L-1}\)

两个数配对指的是异或满足题目

我们可以设 \(f[i]\) 表示 \([1,i]\)\(A_{i+1}\) 的配配对数目所以对于上图的情况有一个\(S\) 可以用 \(f\) 来表示,还有一个就再次离线,到对应的点上。

求出 \(f\) 数组

vector<int>nums;
for (int i = 0; i < (1 << 14); i++)
	if (count(i) == k)nums.push_back(i);

for (int i = 1; i <= n; i++) {
	for (int v : nums)g[w[i] ^ v]++;
	f[i] = g[w[i + 1]];
}

举例当 \(r < qr\)

if (r < qr)subs[l - 1].push_back({ i, r + 1, qr, -1 });
while (r < qr) q[i].res += f[r++];

每一次挪动都要加上 \(f[r]\) 还要减去区间 \([1,l-1]\)\(A_{r+1},A_{r+2},...,A_{qr}\) 的配对数目,对于这个东西,可以把它挂到 \(l-1\) 上, 这个挂挺形象的。

后面挪 \(l\) 的时候要注意自己跟自己匹配的情况,只有 \(k == 0\) 的时候自己可以匹配自己。

注意自己是不能匹配自己的,但是这种情况在计算 \(S\) 的时候是被计算在内的。

最后处理离线查询

memset(g, 0, sizeof g);
for (int i = 1; i <= n; i++) {
	for (int v : nums)g[v ^ w[i]]++;
	for (auto& x : subs[i]) {
		for (int i = x.l; i <= x.r; i++) {
			q[x.id].res += x.t * g[w[i]];
		}
	}
}

这一块的复杂度是 \(r\) 的移动次数 \(O(n \sqrt n)\) 量级

/*
 * @Author: zhl
 * @Date: 2020-11-19 20:16:41
 */
#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N = 1e5 + 10;

int n, m, k, len, ID[N], f[N], g[N], w[N];
ll ans[N];
struct Query {
	int id, l, r;
	ll res;
	bool operator < (const Query& b)const {
		if (ID[l] != ID[b.l]) return ID[l] < ID[b.l];
		return r < b.r;
	}
}q[N];

struct subquery {
	int id, l, r, t;
};
vector<subquery>subs[N];

int count(int n) {
	int ans = 0;
	while (n) ans++, n -= -n & n;
	return ans;
}
int main() {
	scanf("%d%d%d", &n, &m, &k);
	for (int i = 1; i <= n; i++)scanf("%d", w + i);

	len = sqrt(n);
	for (int i = 1; i <= n; i++)ID[i] = i / len;

	vector<int>nums;
	for (int i = 0; i < (1 << 14); i++)if (count(i) == k)nums.push_back(i);

	for (int i = 1; i <= n; i++) {
		for (int v : nums)g[w[i] ^ v]++;
		f[i] = g[w[i + 1]];
	}

	for (int i = 1; i <= m; i++) {
		scanf("%d%d", &q[i].l, &q[i].r); q[i].id = i;
	}

	sort(q + 1, q + 1 + m);

	int l = 1, r = 0;
	for (int i = 1; i <= m; i++) {
		int ql = q[i].l, qr = q[i].r;
		//在 l - 1 处 挂上一个离线查询
		if (r < qr)subs[l - 1].push_back({ i, r + 1, qr, -1 });
		while (r < qr) q[i].res += f[r++];

		if (r > qr)subs[l - 1].push_back({ i, qr + 1,r ,1 });
		while (r > qr) q[i].res -= f[--r];

		if (l < ql)subs[r].push_back({ i, l, ql - 1, -1 });
		while (l < ql) q[i].res += f[l - 1] + !k, l++;

		if (l > ql)subs[r].push_back({ i, ql, l - 1,1 });
		while (l > ql) q[i].res -= f[l - 2] + !k, l--;
	}
	memset(g, 0, sizeof g);
	for (int i = 1; i <= n; i++) {
		for (int v : nums)g[v ^ w[i]]++;
		for (auto& x : subs[i]) {
			for (int i = x.l; i <= x.r; i++) {
				q[x.id].res += x.t * g[w[i]];
			}
		}
	}
	for (int i = 2; i <= m; i++) q[i].res += q[i - 1].res;
	for (int i = 1; i <= m; i++)ans[q[i].id] = q[i].res;
	for (int i = 1; i <= m; i++)printf("%lld\n", ans[i]);
}

posted @ 2020-11-24 21:26  —O0oO-  阅读(163)  评论(0编辑  收藏  举报