求 (n) 元数列的 (k) 个不同的子区间使得各个子区间异或和之和最大。
Solution
(差点又看错题了)
做个前缀和,于是转化成求序列异或和最大的 (k) 个数对
建一棵可持久化 0-1 Trie,这样我们就可以 (O(log n)) 求出对于某个右端点,它的所有可能答案中,第 (k) 大的答案
然后利用堆来维护答案。我们先把对每一个右端点,第 (1) 大的答案插入堆。然后循环弹出。每次弹出一个,如果它是 (u) 这个右端点对应的第 (v) 大的答案,我们就计算出 (u) 这个右端点对应的第 (v+1) 大的答案(如果存在的话)并且把它插入堆,然后继续。
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1000005;
namespace trie {
int ind,ch[N*40][2],siz[N*40],root[N*40];
void insert(int id,int x) {
root[id]=++ind;
ch[ind][0]=ch[root[id-1]][0];
ch[ind][1]=ch[root[id-1]][1];
siz[ind]=siz[root[id-1]]+1;
int p=ind;
for(int i=39;i>=0;--i) {
int t=(x>>i)&1;
++ind;
ch[ind][0]=ch[ch[p][t]][0];
ch[ind][1]=ch[ch[p][t]][1];
siz[ind]=siz[ch[p][t]]+1;
ch[p][t]=ind;
p=ind;
}
}
int query(int id,int x,int k) {
int p=root[id],ans=0;
for(int i=39;i>=0;--i) {
int t=(x>>i)&1;
if(siz[ch[p][t^1]]>=k) {
p=ch[p][t^1];
ans|=1ll<<i;
}
else {
k-=siz[ch[p][t^1]];
p=ch[p][t];
}
}
return ans;
}
}
struct pii {
int a,b,c;
bool operator < (const pii &x) const {
return a < x.a;
}
};
priority_queue <pii> q;
int n,k,a[N],ans;
signed main() {
scanf("%lld%lld",&n,&k);
for(int i=1;i<=n;i++) {
scanf("%lld",&a[i]);
a[i]^=a[i-1];
}
for(int i=0;i<=n;i++) {
trie::insert(i,a[i]);
}
for(int i=1;i<=n;i++) {
q.push((pii){trie::query(i-1,a[i],1),i,1});
//cout<<trie::query(i-1,a[i],1)<<" ";
}
//cout<<endl;
for(int i=1;i<=k;i++) {
int val=q.top().a,pos=q.top().b,idx=q.top().c;
ans+=val;
//cout<<val<<endl;
q.pop();
if(idx<pos) {
q.push((pii){trie::query(pos-1,a[pos],idx+1),pos,idx+1});
}
}
cout<<ans;
}