『异或粽子 堆 可持久化trie』

<更新提示>

<第一次更新>


<正文>

异或粽子

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\n",ans);
    return 0;
}

<后记>

posted @ 2019-07-16 20:04  Parsnip  阅读(220)  评论(0编辑  收藏  举报