problem
小粽是一个喜欢吃粽子的好孩子。今天她在家里自己做起了粽子。
小粽面前有 n n n 种互不相同的粽子馅儿,小粽将它们摆放为了一排,并从左至右编号为 1 1 1 到 n n n。
第 i i i 种馅儿具有一个非负整数的属性值 a i a_i ai。
每种馅儿的数量都足够多,即小粽不会因为缺少原料而做不出想要的粽子。
小粽准备用这些馅儿来做出 k k k 个粽子。
小粽的做法是:选两个整数数 l , r l,r l,r,满足 1 ⩽ l ⩽ r ⩽ n 1 \leqslant l \leqslant r \leqslant n 1⩽l⩽r⩽n,将编号在 [ l , r ] [l, r] [l,r] 范围内的所有馅儿混合做成一个粽子,所得的粽子的美味度为这些粽子的属性值的异或和。
小粽想品尝不同口味的粽子,因此它不希望用同样的馅儿的集合做出一个以上的粽子。
小粽希望她做出的所有粽子的美味度之和最大。请你帮她求出这个值吧!
solution
考虑如何快速求得对于 i i i 而言的一段往前的连续段异或最大值。
显然前缀异或和后,转化成求 j < i , a j ⊕ a i j<i,a_j\oplus a_i j<i,aj⊕ai 的最大值。
字典树基本应用。
接下来又怎么办呢?这么多个区间。
考虑暴力是把所有区间异或和求出来然后扔进堆里面,选权值前 k k k 大的。
这里我们找到了对于每个 i i i 的最佳位置 j j j,但是还有可能第二优秀的位置比其它 i i i 的最佳位置权值更大。
我们又要避免找到重复的区间。
这里类比《超级钢琴》的做法。
记录下最佳位置的选择,然后把可选区间 [ l , r ] [l,r] [l,r] 分成两段 [ l , j ] , ( j , r ) [l,j],(j,r) [l,j],(j,r)。
也就需要实现可持久化字典树了。
code
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define maxn 500005
int root[maxn], cnt;
struct tree { int son[2], id, cnt; }t[maxn * 80];
void insert( int lst, int now, int x, int id ) {
root[now] = ++ cnt;
now = root[now], lst = root[lst];
t[now] = t[lst]; t[now].cnt ++;
for( int i = 32;~ i;i -- ) {
int k = x >> i & 1;
t[now].son[k] = ++ cnt;
now = t[now].son[k];
lst = t[lst].son[k];
t[now] = t[lst];
t[now].cnt ++;
}
t[now].id = id;
}
int query( int L, int R, int x ) {
for( int i = 32;~ i;i -- ) {
int k = x >> i & 1;
if( t[t[R].son[k ^ 1]].cnt - t[t[L].son[k ^ 1]].cnt )
R = t[R].son[k ^ 1], L = t[L].son[k ^ 1];
else
R = t[R].son[k], L = t[L].son[k];
}
return t[R].id;
}
struct node {
int l, r, x, p, val;
bool operator < ( const node &v ) const {
return val < v.val;
}
};
int a[maxn];
int n, k;
priority_queue < node > q;
signed main() {
scanf( "%lld %lld", &n, &k ); n ++;
for( int i = 2;i <= n;i ++ ) scanf( "%lld", &a[i] );
for( int i = 2;i <= n;i ++ ) a[i] ^= a[i - 1];
insert( 0, 1, 0, 1 );
for( int i = 2;i <= n;i ++ ) insert( i - 1, i, a[i], i );
for( int i = 2;i <= n;i ++ ) {
int x = query( root[0], root[i], a[i] );
q.push( (node){ 0, i, x, i, a[i] ^ a[x] } );
}
int ans = 0;
while( ! q.empty() and k ) {
node now = q.top(); q.pop();
k --; ans += now.val;
if( now.x - 1 > now.l ) {
int x1 = query( root[now.l], root[now.x - 1], a[now.p] );
q.push( (node){ now.l, now.x - 1, x1, now.p, a[now.p] ^ a[x1] } );
}
if( now.x < now.r ) {
int x2 = query( root[now.x], root[now.r], a[now.p] );
q.push( (node){ now.x, now.r, x2, now.p, a[now.p] ^ a[x2] } );
}
}
printf( "%lld\n", ans );
return 0;
}