1. 程式人生 > 其它 >異或粽子

異或粽子

題面

異或粽子

題解

將題意轉化一下就是,對於一個大小為 \(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;
}