LOJ3048 「十二省联考 2019」异或粽子

问题描述

小粽是一个喜欢吃粽子的好孩子。今天她在家里自己做起了粽子。

小粽面前有 \(n\) 种互不相同的粽子馅儿,小粽将它们摆放为了一排,并从左至右编号为 \(1\) 到 \(n\)。第 \(i\) 种馅儿具有一个非负整数的属性值 \(a_i\)。每种馅儿的数量都足够多,即小粽不会因为缺少原料而做不出想要的粽子。小粽准备用这些馅儿来做出 \(k\) 个粽子。

小粽的做法是:选两个整数数 \(l,r\),满足 \(1\le l\le r\le n\),将编号在 \([l,r]\) 范围内的所有馅儿混合做成一个粽子,所得的粽子的美味度为这些粽子的属性值的异或和。(异或就是我们常说的 \(\mathrm{xor}\) 运算,即 C/C++ 中的 ^ 运算符或 Pascal 中的 xor 运算符)

小粽想品尝不同口味的粽子,因此它不希望用同样的馅儿的集合做出一个以上的粽子。

小粽希望她做出的所有粽子的美味度之和最大。请你帮她求出这个值吧!


题解

分类考虑,动态用堆维护前 \(k\) 大的

显然,题目中涉及到求 \(a_l \operatorname{xor} a_{l+1} \operatorname{xor} \cdots \operatorname{xor} a_r\) 的操作,则一定需要前缀异或

设 \(S(i)=a_1 \operatorname{xor} a_2 \operatorname{xor} \cdots \operatorname{xor} a_i\),题意则转化为求 \(S(i) \operatorname{xor} S(j)(i<j)\) 的前 \(k\) 大值的和。

这是一个倒三角求值,可以补为正方形,即 \(S(i) \operatorname{xor} S(j)\) 前 \(2k\) 大值的和。

建立一个堆,一开始把 \(\forall i \in [1,n],S(i) \operatorname{xor} S(j)(i \neq j)\) 的最大值放到堆里。

每次取堆顶,假设为 \(S(i)\) 的 \(k\) 大值,则 pop 之后把 \(S(i)\) 的第 \(k+1\) 大值入堆。

以上操作可以通过 \(\texttt{0-1 Trie}\) 完成。


\(\texttt{Code}\)

#include<bits/stdc++.h>
using namespace std;

#define int long long

const int maxn = 500000 + 7;

int n, k;
int a[maxn], s[maxn];
int ans;

int ch[10000000][2], tot, size[10000000];

//priority_queue <node> q;

struct node {
	int val, rk, id;
	bool operator < (const node a) const{
		return val < a.val;
	}
};

priority_queue <node> q;

void Init(void) {
    scanf("%lld%lld", &n, &k); k <<= 1;
    for(int i = 1; i <= n; i++) {
		scanf("%lld", &a[i]);
		s[i] = s[i - 1] ^ a[i];
	}
}

void insert(int x) {
	int p = 0;
	for(int i = 31; i >= 0; i--) {
		int d = (x >> i) & 1;
		size[p] ++;
		if(!ch[p][d]) ch[p][d] = ++tot;
		p = ch[p][d];
	}
	size[p]++;
}

int query(int x, int t) {
	int p = 0, res = 0;
    for(int i = 31; i >= 0; i--) {
		int d = (x >> i) & 1;
		if(ch[p][d ^ 1] == 0) p = ch[p][d];
		else if(t <= size[ch[p][d ^ 1]]) p = ch[p][d ^ 1], res += (1ll << i);
		else t -= size[ch[p][d ^ 1]], p = ch[p][d];
	}
    return res;
}

void Work(void) {
    for(int i = 0; i <= n; i++) {
		insert(s[i]);
	}
	for(int i = 0; i <= n; i++) {
		int rec = query(s[i], 1);
		q.push((node){rec, 1, i});
	}
	while(k--) {
		node r = q.top(); q.pop();
		ans += r.val;
		if(r.rk < n) q.push((node){query(s[r.id], r.rk + 1), r.rk + 1, r.id});

	}
	ans >>= 1;
	printf("%lld\n", ans);
}

signed main(void) {
	Init();
	Work();
	return 0;
}
上一篇:2019牛客暑期多校训练营(第一场)H XOR【线性基】


下一篇:#最小生成树,Trie,启发式合并#CF888G Xor-MST