异或粽子
时间限制: 2 Sec 内存限制: 512 MB
题目描述
因为考前一天发现T3是去年D班考过的题, 所以liu_runda决定临时换题, 他找到了十二省联考的包, 发现day1t1搬过来很合适, 就开始抄题面.
小粽是一个喜欢吃粽子的好孩子。今天她在家里自己做起了粽子。
小粽面前有n 种互不相同的粽子馅儿,小粽将它们摆放为了一排,并从左至右编号为1 到n。第i 种馅儿具有一个非负整数的属性值ai。每种馅儿的数量都足够多,即小粽不会因为缺少原料而做不出想要的粽子。小粽准备用这些馅儿来做出k 个粽子。
小粽的做法是:选两个整数数l, r,满足1 ≤ l ≤ r ≤ n,将编号在[l, r] 范围内的所有馅儿混合做成一个粽子,所得的粽子的美味度为这些粽子的属性值的异或和。(异或就是我们常说的xor 运算,即C/C++ 中的 ˆ 运算符)
小粽想品尝不同口味的粽子,因此它不希望用同样的馅儿的集合做出一个以上的粽子。小粽希望她做出的所有粽子的美味度之和最大。请你帮她求出这个值吧!
输入
第一行两个正整数n, k,表示馅儿的数量,以及小粽打算做出的粽子的数量。
接下来一行为n 个非负整数,第i 个数为ai,表示第i 个粽子的属性值。
对于所有的输入数据都满足:1 ≤ n ≤ 5 × 105, 1 ≤ k ≤ min{n(n−1)/2 , 2 × 105}, 0 ≤ ai ≤4, 294, 967, 295。
输出
输出一行一个整数,表示小粽可以做出的粽子的美味度之和的最大值。
样例输入
3 2
1 2 3
样例输出
6
思路
最直接的想法都是n2的,但是显然这个数据范围需要其他的思路。
考虑取最大的一项,可以通过前缀和+trie搞定(trie从高位到低位,每次贪心走尽可能与ask的数不同的那一侧,得到的结果最大)
于是需要通过修改trie的做法使得可以查询第k大的项。
然后用一个堆维护答案,堆内一开始存放每个a[i]的第一大结果,当取出一项时,将第二大结果放入堆中。由于i,j和j,i会被取两次,所以可以总共取出2k次,再将最终答案除以k(好像这个做法以外的方法需要使用可持久化trie)
注意由于我们将区间异或和转化成两个值的异或,所以需要将s[0]=0这一项也一并加入。
代码
#pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
//#include <bits/extc++.h>
//#include <ext/pb_ds/assoc_container.hpp>
//#include <ext/pb_ds/tree_policy.hpp>
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
//using namespace __gnu_pbds;
const int N=5e5+5;
const int M=2e7+5;
int mod=998244353;
//const ll inf=0x3f3f3f3f3f3f3f3f;
const int inf=0x3f3f3f3f;
const double eps=1e-10;
int trie[M][2],sz[M],tot;
void insert(ll x)
{
int now=0;
for(int i=31;i>=0;i--)
{
ll p=(x>>i)&1;
sz[now]++;
if(!trie[now][p]) trie[now][p]=++tot;
now=trie[now][p];
}
sz[now]++;
}
ll ask(ll x,int t)
{
int now=0;
ll ans=0;
for(int i=31;i>=0;i--)
{
ll p=(x>>i)&1;
if(!trie[now][!p])
{
now=trie[now][p];
continue;
}
if(t<=sz[trie[now][!p]])
{
now=trie[now][!p];
ans|=1ll<<i;
} else
{
t-=sz[trie[now][!p]];
now=trie[now][p];
}
}
return ans;
}
ll a[N],b[N];
struct data
{
int id,rk;
ll x;
};
bool operator <(data a,data b)
{
return a.x<b.x;
}
priority_queue<data>q;
int main() {
#ifdef DEBUG
freopen("in", "r", stdin);
#endif
ios::sync_with_stdio(0);
cin.tie(0);
ll n,k;
cin>>n>>k;
k*=2;
insert(0);
for(int i=1;i<=n;i++)
cin>>b[i],a[i]=a[i-1]^b[i],insert(a[i]);
for(int i=0;i<=n;i++)
{
q.push({i,1,ask(a[i],1)});
}
ll ans=0;
ask(a[0],3);
while(k--)
{
data now=q.top();
q.pop();
ans+=now.x;
// cout<<now.id<<' '<<now.rk<<' '<<now.x<<endl;
if(now.rk<n) q.push({now.id,now.rk+1,ask(a[now.id],now.rk+1)});
}
cout<<ans/2;
return 0;
}