题目大意
传送门
给定一个长为n的序列, 最大化选出k个不同区间异或和的和
(n,k<=5*10^5)
solution
先讲套路: 看到这类跟位运算有关的题,先想想Trie树
设 (S_i) 为[1...i]区间的异或和,则任意一个区间[l,r]异或和为 (S_{l-1}) xor (S_r)
考虑每个节点 i 作为右端点时第一次选择可能的贡献:
选出1个节点j (j<=i) 作为左端点: 最大化 (S_j xor S_i)
然后需要在所有节点中找出贡献最大的节点
那么下一次该点i可能的有贡献点对就必须是 选出j,满足 (S_j xor S_i) 为次大值
以此类推...
所以我们建一个大根堆,先把所有点作为右端点的最大贡献点对加入
然后对每个节点为记录其使用过了几次
每删除一个堆顶,记录贡献之后
若该点已使用cnt次,就将以该点为右端点的第cnt大贡献点对加入
code
#include<bits/stdc++.h>
using namespace std;
#define re register
#define ll long long
#define get getchar()
#define in inline
#define int unsigned
in int read()
{
int t=0; char ch=get;
while(ch<'0' || ch>'9') ch=get;
while(ch<='9' && ch>='0') t=t*10+ch-'0', ch=get;
return t;
}
const int _=1e6+23;
struct Trie{
int siz,ch[2];
}tr[_<<6];
int rt[_<<2], tot, s[_], n, m,now[_];
#define ls(k) tr[k].ch[c]
#define rs(k) tr[k].ch[1^c]
in void insert(int a,short x,int k1,int k2)
{
if(x<0) { tr[k2].siz=tr[k1].siz+1; return;}
int c=a>>x&1;
if(k1) rs(k2)=rs(k1);
ls(k2)=++tot;
insert(a,x-1,ls(k1),ls(k2));
tr[k2].siz=tr[ls(k2)].siz+tr[rs(k2)].siz;
}
in int query(int a,short x,int k,int lim)
{
if(x<0){ return 0;}
int c=a>>x&1;
if(tr[rs(k)].siz>=lim) return query(a,x-1,rs(k),lim)|(1<<x);
else return query(a,x-1,ls(k),lim-tr[rs(k)].siz);
}
#define mp make_pair
signed main()
{
#ifndef ONLINE_JUDGE
freopen("1.in","r",stdin);
#endif
n=read(), m=read();
rt[0]=++tot;
insert(0,31,0,rt[0]);
for(re int i=1;i<=n;++i)
{
int x=read(); s[i]=s[i-1]^x;
rt[i]=++tot;
insert(s[i],31,rt[i-1],rt[i]);
}
ll ans=0; priority_queue<pair<int,int> >q; //第一维是贡献,第二维是区间右端点
for(re int i=1;i<=n;++i) {q.push(mp(query(s[i],31,rt[i],1),i)); now[i]=1;}
while(m--)
{
ans+=q.top().first; int u=q.top().second; q.pop();
q.push(mp(query(s[u],31,rt[u],++now[u]),u));
}
cout<<ans<<endl;
return 0;
}