题目描述
小粽是一个喜欢吃粽子的好孩子。今天她在家里自己做起了粽子。
小粽面前有 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;
}
来源:CSDN
作者:青烟绕指柔!
链接:https://blog.csdn.net/weixin_43826249/article/details/103962294