<更新提示>
<第一次更新>
<正文>
异或粽子
Description
小粽是一个喜欢吃粽子的好孩子。今天她在家里自己做起了粽子。
小粽面前有 n 种互不相同的粽子馅儿,小粽将它们摆放为了一排,并从左至右编号为 1 到 n。第 i 种馅儿具有一个非负整数的属性值 ai。每种馅儿的数量都足够多,即小粽不 会因为缺少原料而做不出想要的粽子。小粽准备用这些馅儿来做出 k 个粽子。
小粽的做法是:选两个整数数 l, r,满足 1 ≤ l ≤ r ≤ n,将编号在[l, r] 范围内的所有 馅儿混合做成一个粽子,所得的粽子的美味度为这些粽子的属性值的异或和。(异或就 是我们常说的 xor 运算,即 C/C++ 中的 ˆ 运算符)
小粽想品尝不同口味的粽子,因此它不希望用同样的馅儿的集合做出一个以上的粽 子。小粽希望她做出的所有粽子的美味度之和最大。请你帮她求出这个值吧!
Input Format
第一行两个正整数 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。
Output Format
输出一行一个整数,表示小粽可以做出的粽子的美味度之和的最大值。
Sample Input
3 2
1 2 3
Sample Output
6
解析
题目大意:给定一个长度为(n)的序列,求序列异或和最大的(k)个子序列的异或值之和。
枚举(n^2)个子区间显然不现实,注意到(k)与(n)同阶,于是想到直接去找那(k)个区间。不妨对区间分分类:我们以右端点为分类的依据,将所有子区间分为(n)类。显然,我们可以先处理处每一类区间中异或和最大的一个,然后取每一类中最大的,这样在全局上一定也是最优的。
记三元组(([x,y],l,r))代表右端点(r),左端点取值范围为([x,y])的这一类区间,其中,左端点取(l)时异或和最大。这时,我们只需把所有三元组塞进一个大根堆里,取异或值最大的就是当前的一个全局最优解。
当我们取了一个目前值最大的区间后,显然这一类中区间还有其他方案可以选择,我们不能取完后直接将整个三元组剔除。根据定义,我们可以简单的将这个三元组分解为另两个格式相同的三元组(([x,l-1],l_1,r))和(([l+1,r],l_2,r)),这样刚好包含了这类区间中左端点取其他值的情况,又可以加回堆中。
那么问题就只剩下了给定一个右端点和左端点的取值范围,如何快速求最佳的左端点位置,使得区间异或和最大,发现这是可持久化(0/1trie)的基本功能。
(Code:)
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5+20;
struct node { int x,y,l,r; long long val; };
int n,k,cnt,root[N],Trie[N*33+N][2],latest[N*33+N];
long long ans,a[N],s[N];
inline int read(void)
{
int x = 0 , w = 0; char ch = ' ';
while ( !isdigit(ch) ) w |= ch=='-' , ch = getchar();
while ( isdigit(ch) ) x = x*10 + ch-48 , ch = getchar();
return w ? -x : x;
}
inline long long readll(void)
{
long long x = 0 , w = 0; char ch = ' ';
while ( !isdigit(ch) ) w |= ch=='-' , ch = getchar();
while ( isdigit(ch) ) x = x*10 + ch-48 , ch = getchar();
return w ? -x : x;
}
void insert(int id,int k,int p,int q)
{
if ( k < 0 ) { latest[q] = id; return; }
int c = s[id] >> k & 1;
if ( p != 0 ) Trie[q][c^1] = Trie[p][c^1];
Trie[q][c] = ++cnt;
insert( id , k-1 , Trie[p][c] , Trie[q][c] );
latest[q] = max( latest[ Trie[q][0] ] , latest[ Trie[q][1] ] );
}
int query(int now,long long val,int k,int lim)
{
if ( k < 0 ) return latest[now];
int c = val >> k & 1;
if ( latest[Trie[now][c^1]] >= lim )
return query( Trie[now][c^1] , val , k-1 , lim );
else return query( Trie[now][c] , val , k-1 , lim );
}
inline void init(void)
{
latest[0] = -1 , root[0] = ++cnt;
insert( 0 , 32 , 0 , root[0] );
for ( register int i = 1 ; i <= n ; i++ )
{
s[i] = s[i-1] ^ a[i] , root[i] = ++cnt;
insert( i , 32 , root[i-1] , root[i] );
}
}
inline void input(void)
{
n = read() , k = read();
for ( int i = 1 ; i <= n ; i++ )
a[i] = readll();
}
inline bool operator < (node p1,node p2) { return p1.val < p2.val; }
inline void solve(void)
{
priority_queue < node > Heap;
for ( register int i = 1 ; i <= n ; i++ )
{
int pos = query( root[i-1] , s[i] , 32 , 0 );
Heap.push( (node){ 0 , i-1 , pos , i , s[pos] ^ s[i] } );
}
while ( k-- )
{
node t = Heap.top(); Heap.pop();
ans += t.val;
int l , r , pos;
l = t.x , r = t.l - 1;
if ( l <= r )
{
pos = query( root[r] , s[t.r] , 32 , l );
Heap.push( (node){ l , r , pos , t.r , s[pos] ^ s[t.r] } );
}
l = t.l + 1 , r = t.y;
if ( l <= r )
{
pos = query( root[r] , s[t.r] , 32 , l );
Heap.push( (node){ l , r , pos , t.r , s[pos] ^ s[t.r] } );
}
}
}
int main(void)
{
freopen("xor.in","r",stdin);
freopen("xor.out","w",stdout);
input();
init();
solve();
printf("%lld
",ans);
return 0;
}
<后记>