14938异或粽子

异或粽子
时间限制: 2 Sec 内存限制: 512 MB
题目描述
因为考前一天发现T3是去年D班考过的题, 所以liu_runda决定临时换题, 他找到了十二省联考的包, 发现day1t1搬过来很合适, 就开始抄题面.
小粽是一个喜欢吃粽子的好孩子。今天她在家里自己做起了粽子。
小粽面前有n 种互不相同的粽子馅儿,小粽将它们摆放为了一排,并从左至右编号为1 到n。第i 种馅儿具有一个非负整数的属性值ai。每种馅儿的数量都足够多,即小粽不会因为缺少原料而做不出想要的粽子。小粽准备用这些馅儿来做出k 个粽子。
小粽的做法是:选两个整数数l, r,满足1 ≤ l ≤ r ≤ n,将编号在[l, r] 范围内的所有馅儿混合做成一个粽子,所得的粽子的美味度为这些粽子的属性值的异或和。(异或就是我们常说的xor 运算,即C/C++ 中的 ˆ 运算符)
小粽想品尝不同口味的粽子,因此它不希望用同样的馅儿的集合做出一个以上的粽子。小粽希望她做出的所有粽子的美味度之和最大。请你帮她求出这个值吧!

输入
第一行两个正整数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。

输出
输出一行一个整数,表示小粽可以做出的粽子的美味度之和的最大值。
样例输入
3 2
1 2 3
样例输出
6

思路

最直接的想法都是n2的,但是显然这个数据范围需要其他的思路。
考虑取最大的一项,可以通过前缀和+trie搞定(trie从高位到低位,每次贪心走尽可能与ask的数不同的那一侧,得到的结果最大)
于是需要通过修改trie的做法使得可以查询第k大的项。
然后用一个堆维护答案,堆内一开始存放每个a[i]的第一大结果,当取出一项时,将第二大结果放入堆中。由于i,j和j,i会被取两次,所以可以总共取出2k次,再将最终答案除以k(好像这个做法以外的方法需要使用可持久化trie)
注意由于我们将区间异或和转化成两个值的异或,所以需要将s[0]=0这一项也一并加入。

代码

#pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
//#include <bits/extc++.h>
//#include <ext/pb_ds/assoc_container.hpp>
//#include <ext/pb_ds/tree_policy.hpp>
 
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
//using namespace __gnu_pbds;
const int N=5e5+5;
const int M=2e7+5;
int mod=998244353;
//const ll inf=0x3f3f3f3f3f3f3f3f;
const int inf=0x3f3f3f3f;
const double eps=1e-10;
int trie[M][2],sz[M],tot;
void insert(ll x)
{
    int now=0;
    for(int i=31;i>=0;i--)
    {
        ll p=(x>>i)&1;
        sz[now]++;
        if(!trie[now][p]) trie[now][p]=++tot;
        now=trie[now][p];
    }
    sz[now]++;
}
ll ask(ll x,int t)
{
    int now=0;
    ll ans=0;
    for(int i=31;i>=0;i--)
    {
        ll p=(x>>i)&1;
        if(!trie[now][!p])
        {
            now=trie[now][p];
            continue;
        }
        if(t<=sz[trie[now][!p]])
        {
            now=trie[now][!p];
            ans|=1ll<<i;
        } else
        {
            t-=sz[trie[now][!p]];
            now=trie[now][p];
        }
    }
    return ans;
}
ll a[N],b[N];
struct data
{
    int id,rk;
    ll x;
};
bool operator <(data a,data b)
{
    return a.x<b.x;
}
priority_queue<data>q;
int main() {
#ifdef DEBUG
    freopen("in", "r", stdin);
#endif
    ios::sync_with_stdio(0);
    cin.tie(0);
 
    ll n,k;
    cin>>n>>k;
    k*=2;
    insert(0);
    for(int i=1;i<=n;i++)
        cin>>b[i],a[i]=a[i-1]^b[i],insert(a[i]);
    for(int i=0;i<=n;i++)
    {
        q.push({i,1,ask(a[i],1)});
    }
    ll ans=0;
    ask(a[0],3);
    while(k--)
    {
        data now=q.top();
        q.pop();
        ans+=now.x;
//        cout<<now.id<<' '<<now.rk<<' '<<now.x<<endl;
        if(now.rk<n) q.push({now.id,now.rk+1,ask(a[now.id],now.rk+1)});
    }
    cout<<ans/2;
 
    return 0;
 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值