异或粽子

2021/7/28 23:11:02

本文主要是介绍异或粽子,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

题面

异或粽子

题解

将题意转化一下就是,对于一个大小为 \(n\) 的数组,求出前 \(k\) 大的不重复的区间异或和的和。
我们记录一下区间前缀异或和。那么一个区间的异或和就可以表示为 \(sum[r]\ xor\ sum[l - 1]\)。
那么我们要求的就是形如这样的最大的 \(k\) 对异或值的和。即 \(sum[i]\ xor\ sum[j] (0\leq j < i \leq n)\)。
也就是在一个大小为 \(n + 1\) 的数组中的 \(\frac{n(n +1)}{2}\) 中组合中选出前 \(k\) 大的。
而这些组合以 \(i, j\) 为长宽的话,对应着一个矩形的情况,而在上述的范围中是一个不包含对角线的类似于三角形的形状。
为了使我们寻找更方便,又因为 \(sum[i]\ xor\ sum[j] = sum[j] \ xor \ sum[i]\) 所以我们可以将另一半补全,寻找两部分中前 \(2k\) 大的值,而对角线上的值显然是 \(0\) 因为答案保证了 \(k \leq \frac{n(n - 1)}{2}\),所以对角线上的值不可能被选择,所以我们可以直接将矩阵补全,所以就不用考虑 \(i, j\) 之间的关系了。
建立一个 \(Trie\) 树,维护某个数与集合中的某个数排名为 \(rk\) 的异或值。
建立一个大根堆,将 \(0\) ~ \(n\) 的数与集合中的某个数排名为 \(1\) 的异或值,即它的排名插入堆中。
每次取出堆顶,将当前排名的下一个排名插入堆中,取完 \(2k\) 个元素后就得到了前 \(2k\) 大的元素。
因为每次插入堆中的都是每个元素与其它元素的组合中未被选择的最大异或值,而大根堆维护了这些组合中的最大值,所以取完 \(2k\) 个元素后就得到了前 \(2k\) 大的元素。

代码

#include<cstdio>
#include<queue>

using namespace std;

typedef long long LL;

const int N = 5e5 + 5;

int n, m; LL a[N];

struct data {
	int id, rk; LL val;
	data(int id_ = 0, int rk_ = 0, LL val_ = 0) : id(id_), rk(rk_), val(val_) {}
	bool operator < (const data &x) const {
		return val < x.val;
	}
};

priority_queue < data > q;

struct Trie {
	#define M N * 40
	int nex[M][2], siz[M], cnt;
	Trie () { cnt = 0; }
	void insert(LL x) {
		int p = 0;
		for(int i = 31; ~i; i--) {
			int ch = (x >> i) & 1; siz[p]++;
			if(!nex[p][ch]) nex[p][ch] = ++cnt;
			p = nex[p][ch];
		}
		siz[p]++;
	}
	LL query(LL x, int rk) {
		int p = 0; LL ans = 0;
		for(int i = 31; ~i; i--) {
			int ch = (x >> i) & 1;
			if(!nex[p][ch ^ 1]) p = nex[p][ch];
			else if(rk <= siz[nex[p][ch ^ 1]]) p = nex[p][ch ^ 1], ans |= 1LL << i;
			else rk -= siz[nex[p][ch ^ 1]], p = nex[p][ch];
		}
		return ans;
	}
}tr;

int main() {
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; i++) scanf("%lld", &a[i]), a[i] = a[i] ^ a[i - 1];
	for(int i = 0; i <= n; i++) tr.insert(a[i]);
	for(int i = 0; i <= n; i++) q.push(data(i, 1, tr.query(a[i], 1)));
	m <<= 1; LL res = 0;
	for(int i = 1; i <= m; i++) {
		data x = q.top(); res += x.val; q.pop();
		if(x.rk < n) q.push(data(x.id, x.rk + 1, tr.query(a[x.id], x.rk + 1)));
	}
	printf("%lld\n", res >> 1);
	return 0;
}


这篇关于异或粽子的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程