[十二省联考2019]异或粽子

╄→尐↘猪︶ㄣ 提交于 2020-01-29 08:17:33

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

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

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

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

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

输入格式
第一行两个正整数 n, k,表示馅儿的数量,以及小粽打算做出的粽子的数量。

输出格式
输出一行一个整数,表示小粽可以做出的粽子的美味度之和的最大值。

输入输出样例
输入 #1复制
3 2
1 2 3


堆+Trie上第k大。

区间的异或,我们很容易通过前缀和转换成可以用Trie查询维护的数据结构。

Trie上怎么找第k大呢?我们类似于平衡树怎么找第k大呢?就是通过维护size,同理Trie维护size也是可以找第k大的。

我们用堆去维护最大值。最开始的最大值肯定是从1到n固定端点之后在Trie上面找的最大值,那么第2大的值要么是之前的最大值,要么是当前答案的第2大。所以一直维护即可。

但是:因为异或是 x ^ y = y ^ x ,导致答案会计算两次,所以我们找 2*k 大的和,最后除以2即可。
当然,直接用可持久化的Trie也可以,就不用除2了。


AC代码:

#pragma GCC optimize("-Ofast","-funroll-all-loops")
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e5+10;
int n,k,t[N*40][2],idx,sz[N*40],res,s[N];
struct node{int id,sum,rk;};
bool operator < (node a,node b){return a.sum<b.sum;}
priority_queue<node> q;
inline void add(int x){
	int p=0;
	for(int i=31;i>=0;i--){
		int k=x>>i&1LL;	sz[p]++;
		if(!t[p][k])	t[p][k]=++idx;
		p=t[p][k];
	}
	sz[p]++;
}
inline int ask(int x,int rk){
	int p=0,res=0;
	for(int i=31;i>=0;i--){
		int k=x>>i&1LL;
		if(!t[p][k^1])	p=t[p][k];
		else if(rk<=sz[t[p][k^1]])	p=t[p][k^1],res|=(1LL<<i);
		else	rk-=sz[t[p][k^1]],p=t[p][k];
	}
	return res;
}
signed main(){
	ios::sync_with_stdio(false);	cin.tie(nullptr);
	cin>>n>>k;	k<<=1LL;	add(0);
	for(int i=1;i<=n;i++)	cin>>s[i],s[i]^=s[i-1],add(s[i]);
	for(int i=0;i<=n;i++)	q.push({i,ask(s[i],1),1});
	while(k--){
		node u=q.top();	q.pop(); res+=u.sum;
		if(u.rk<n)	q.push({u.id,ask(s[u.id],u.rk+1),u.rk+1});
	}
	cout<<(res>>1LL);
	return 0;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!