[十二省联考 2019] 异或粽子
题目描述
小粽是一个喜欢吃粽子的好孩子。今天她在家里自己做起了粽子。
小粽面前有 n n n 种互不相同的粽子馅儿,小粽将它们摆放为了一排,并从左至右编号为 1 1 1 到 n n n。第 i i i 种馅儿具有一个非负整数的属性值 a i a_i ai。每种馅儿的数量都足够多,即小粽不会因为缺少原料而做不出想要的粽子。小粽准备用这些馅儿来做出 k k k 个粽子。
小粽的做法是:选两个整数数
l
l
l,
r
r
r,满足
1
⩽
l
⩽
r
⩽
n
1 \leqslant l \leqslant r \leqslant n
1⩽l⩽r⩽n,将编号在
[
l
,
r
]
[l, r]
[l,r] 范围内的所有馅儿混合做成一个粽子,所得的粽子的美味度为这些粽子的属性值的异或和。(异或就是我们常说的 xor 运算,即 C/C++ 中的 ˆ
运算符或 Pascal 中的 xor
运算符)
小粽想品尝不同口味的粽子,因此它不希望用同样的馅儿的集合做出一个以上的
粽子。
小粽希望她做出的所有粽子的美味度之和最大。请你帮她求出这个值吧!
输入格式
第一行两个正整数 n n n, k k k,表示馅儿的数量,以及小粽打算做出的粽子的数量。
接下来一行为
n
n
n 个非负整数,第
i
i
i 个数为
a
i
a_i
ai,表示第
i
i
i 个粽子的属性值。
对于所有的输入数据都满足:
1
⩽
n
⩽
5
×
1
0
5
1 \leqslant n \leqslant 5 \times 10^5
1⩽n⩽5×105,
1
⩽
k
⩽
min
{
n
(
n
−
1
)
2
,
2
×
1
0
5
}
1 \leqslant k \leqslant \min\left\{\frac{n(n-1)}{2},2 \times 10^{5}\right\}
1⩽k⩽min{2n(n−1),2×105},
0
⩽
a
i
⩽
4294967295
0 \leqslant a_i \leqslant 4 294 967 295
0⩽ai⩽4294967295。
输出格式
输出一行一个整数,表示小粽可以做出的粽子的美味度之和的最大值。
样例 #1
样例输入 #1
3 2
1 2 3
样例输出 #1
6
提示
测试点 | n n n | k k k |
---|---|---|
1 1 1, 2 2 2, 3 3 3, 4 4 4, 5 5 5, 6 6 6, 7 7 7, 8 8 8 | ⩽ 1 0 3 \leqslant 10^3 ⩽103 | ⩽ 1 0 3 \leqslant 10^3 ⩽103 |
9 9 9, 10 10 10, 11 11 11, 12 12 12 | ⩽ 5 × 1 0 5 \leqslant 5 \times 10^5 ⩽5×105 | ⩽ 1 0 3 \leqslant 10^3 ⩽103 |
13 13 13, 14 14 14, 15 15 15, 16 16 16 | ⩽ 1 0 3 \leqslant 10^3 ⩽103 | ⩽ 2 × 1 0 5 \leqslant 2 \times 10^5 ⩽2×105 |
17 17 17, 18 18 18, 19 19 19, 20 20 20 | ⩽ 5 × 1 0 5 \leqslant 5 \times 10^5 ⩽5×105 | ⩽ 2 × 1 0 5 \leqslant 2 \times 10^5 ⩽2×105 |
思路
这道题首先是要求区间的异或最大值以及前k大,我们看见区间、前k大、第k大而且还是异或我们就得想到可持久化字典树。
- 细节1:即使define 还是要在移位的过程加ll。(大坑点)
代码
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#define int long long
using namespace std;
const int N = 3e7+10;
struct E{
int id,k,w;
bool operator<(const E &t)const{
return w<t.w;
}
};
int son[N][2],idx;
int n,k;
priority_queue<E>q;//存放
int s[N];//区间异或和
int siz[N];
void insert(int x){
int p=0;
for(int i=31;i>=0;i--){
int u=x>>i&1;
siz[p]++;
if(!son[p][u])son[p][u]=++idx;
p=son[p][u];
}
siz[p]++;
}
//不含第k大的写法
// int query(int x){
// int p=0;
// int res=0;
// for(int i=30;i>=0;i--){
// int u=x>>i&1;
// if(son[p][!u]){
// p=son[p][!u];
// res=res*2+!u;
// }else{
// p=son[p][u];
// res=res*2+u;
// }
// }
// return res;
// }
//含第k大的写法
int query(int x,int k){
int p=0,res=0;
for(int i=31;i>=0;i--){
int u=x>>i&1;
if(!son[p][!u]){
p=son[p][u];
}else if(k<=siz[son[p][!u]]){
p=son[p][!u];
res|=1ll<<i;
}else{
k-=siz[son[p][!u]];
p=son[p][u];
}
}
return res;
}
//这个是异或最大值
signed main(){
cin>>n>>k;
k<<=1ll;//前2k个
int res=1;
for(int i=1;i<=n;i++){
int x;
cin>>x;
s[i]=s[i-1]^x;
}
for(int i=0;i<=n;i++){
insert(s[i]);
}
for(int i=0;i<=n;i++){
q.push({i,1,query(s[i],1)});//query(a[i])查找的是a[i]值的最大与或值
//堆中节点存第rk大的s[id]^s[j]
}
for(int i=1;i<=k;i++){
E t=q.top();
q.pop();
int id=t.id,kk=t.k,w=t.w;
// cout<<w<<endl;
res+=w;
if(kk<n)q.push({id,kk+1,query(s[id],kk+1)});
}
cout<<(res>>1ll);
return 0;
}