[十二省联考 2019] 异或粽子(可持久化字典树)

[十二省联考 2019] 异或粽子

题目描述

小粽是一个喜欢吃粽子的好孩子。今天她在家里自己做起了粽子。

小粽面前有 n n n 种互不相同的粽子馅儿,小粽将它们摆放为了一排,并从左至右编号为 1 1 1 n n n。第 i i i 种馅儿具有一个非负整数的属性值 a i a_i ai。每种馅儿的数量都足够多,即小粽不会因为缺少原料而做不出想要的粽子。小粽准备用这些馅儿来做出 k k k 个粽子。

小粽的做法是:选两个整数数 l l l, r r r,满足 1 ⩽ l ⩽ r ⩽ n 1 \leqslant l \leqslant r \leqslant n 1lrn,将编号在 [ l , r ] [l, r] [l,r] 范围内的所有馅儿混合做成一个粽子,所得的粽子的美味度为这些粽子的属性值的异或和。(异或就是我们常说的 xor 运算,即 C/C++ 中的 ˆ 运算符或 Pascal 中的 xor 运算符)

小粽想品尝不同口味的粽子,因此它不希望用同样的馅儿的集合做出一个以上的
粽子。

小粽希望她做出的所有粽子的美味度之和最大。请你帮她求出这个值吧!

输入格式

第一行两个正整数 n n n, k k k,表示馅儿的数量,以及小粽打算做出的粽子的数量。

接下来一行为 n n n 个非负整数,第 i i i 个数为 a i a_i ai,表示第 i i i 个粽子的属性值。
对于所有的输入数据都满足: 1 ⩽ n ⩽ 5 × 1 0 5 1 \leqslant n \leqslant 5 \times 10^5 1n5×105, 1 ⩽ k ⩽ min ⁡ { n ( n − 1 ) 2 , 2 × 1 0 5 } 1 \leqslant k \leqslant \min\left\{\frac{n(n-1)}{2},2 \times 10^{5}\right\} 1kmin{2n(n1),2×105}, 0 ⩽ a i ⩽ 4294967295 0 \leqslant a_i \leqslant 4 294 967 295 0ai4294967295

输出格式

输出一行一个整数,表示小粽可以做出的粽子的美味度之和的最大值。

样例 #1

样例输入 #1

3 2
1 2 3

样例输出 #1

6

提示

测试点 n n n k k k
1 1 1, 2 2 2, 3 3 3, 4 4 4, 5 5 5, 6 6 6, 7 7 7, 8 8 8 ⩽ 1 0 3 \leqslant 10^3 103 ⩽ 1 0 3 \leqslant 10^3 103
9 9 9, 10 10 10, 11 11 11, 12 12 12 ⩽ 5 × 1 0 5 \leqslant 5 \times 10^5 5×105 ⩽ 1 0 3 \leqslant 10^3 103
13 13 13, 14 14 14, 15 15 15, 16 16 16 ⩽ 1 0 3 \leqslant 10^3 103 ⩽ 2 × 1 0 5 \leqslant 2 \times 10^5 2×105
17 17 17, 18 18 18, 19 19 19, 20 20 20 ⩽ 5 × 1 0 5 \leqslant 5 \times 10^5 5×105 ⩽ 2 × 1 0 5 \leqslant 2 \times 10^5 2×105

思路

这道题首先是要求区间的异或最大值以及前k大,我们看见区间、前k大、第k大而且还是异或我们就得想到可持久化字典树。

请添加图片描述

  • 细节1:即使define 还是要在移位的过程加ll。(大坑点)

代码

#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>

#define int long long

using namespace std;

const int N = 3e7+10;

struct E{
    int id,k,w;
    bool operator<(const E &t)const{
        return w<t.w;
    }
};

int son[N][2],idx;
int n,k;
priority_queue<E>q;//存放
int s[N];//区间异或和
int siz[N];

void insert(int x){
    int p=0;
    for(int i=31;i>=0;i--){
        int u=x>>i&1;
        siz[p]++;
        if(!son[p][u])son[p][u]=++idx;
        p=son[p][u];
    }
    siz[p]++;
}
//不含第k大的写法
// int query(int x){
//     int p=0;
//     int res=0;
    
//     for(int i=30;i>=0;i--){
//         int u=x>>i&1;
//         if(son[p][!u]){
//             p=son[p][!u];
//             res=res*2+!u;
//         }else{
//             p=son[p][u];
//             res=res*2+u;
//         }
//     }
//     return res;
// }

//含第k大的写法

int query(int x,int k){
    int p=0,res=0;
    
    for(int i=31;i>=0;i--){
        int u=x>>i&1;
        if(!son[p][!u]){
            p=son[p][u];
        }else if(k<=siz[son[p][!u]]){
            p=son[p][!u];
            res|=1ll<<i;
        }else{
            k-=siz[son[p][!u]];
            p=son[p][u];
        }
    }
    return res;
}

//这个是异或最大值
signed main(){
    cin>>n>>k;
    k<<=1ll;//前2k个
    int res=1;
    for(int i=1;i<=n;i++){
        int x;
        cin>>x;
        s[i]=s[i-1]^x;
    }
    
    for(int i=0;i<=n;i++){
        insert(s[i]);
    }
    
    for(int i=0;i<=n;i++){
        q.push({i,1,query(s[i],1)});//query(a[i])查找的是a[i]值的最大与或值
        //堆中节点存第rk大的s[id]^s[j]
    }
    
    for(int i=1;i<=k;i++){
        E t=q.top();
        q.pop();
        int id=t.id,kk=t.k,w=t.w;
        // cout<<w<<endl;
        res+=w;
        if(kk<n)q.push({id,kk+1,query(s[id],kk+1)});
    }
    
    cout<<(res>>1ll);
    
    return 0;
}
  • 32
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

green qwq

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值