「十二省联考 2019」异或粽子

题目链接

算法:

        首先把前缀异或和统计出来,再将得到的每一个前缀异或和(包括pre[0]=0),塞进字典树中,接下来有一个贪心的思路:每当我拿着其中一个异或和的值时,我在字典树中尽可能找二进制高位与其对应的位不相同的异或和,这样两者异或运算后,所得值最大。

        所以我们有了这样一个思路,对于每一个pre[i](1<=i<=n,忘说了pre[i]=co[1]^co[2]......^co[i]),我们找到以第i位为端点(并不一定是左端点或右端点)的区间中,异或和最大的是多少,并将n个最大值塞入一个堆中。接下来每次操作,我们将n个数中最大的取出,累计入ans,再将该最大值对应的位置,能产生的次小的异或值插入堆中(就是取出以第i位为端点的区间中,第k+1大的异或值)。

        考虑怎么计算第k大,我们可以预处理出字典树每一个子树的大小,那么再利用前面那个思路的性质,可以用类似treap的算法算出第k打的异或和。最后需要注意的是,由于一个区间有两个端点,可能会重复计算一次,那么在这种情况下,我们取前2*k大的异或和,最后答案/2即可。

 

Code:

#include<bits/stdc++.h>
#define rep(i,j,k) for(int i=j;i<=k;i++)
#define rep2(i,j,k) for(int i=j;i>=k;i--)
using namespace std;
template<typename T> void read(T &num){
    char c=getchar();num=0;T f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){num=(num<<3)+(num<<1)+(c^48);c=getchar();}
    num*=f;
}
template<typename T> void qwq(T x){
    if(x>9)qwq(x/10);
    putchar(x%10+'0');
}
template<typename T> void write(T x){
    if(x<0){x=-x;putchar('-');}
    qwq(x);putchar('\n');
}
long long co[500010];long long pre[500010];
struct wzy{
    int pos[2];
}tree[20000010];
int tot=1;int siz[20000010];
inline void change(long long x){
    int ret=1;
    rep2(i,31,0){
        int tmp=(((1ll<<i)&x)!=0);siz[ret]++;
        if(!tree[ret].pos[tmp])tree[ret].pos[tmp]=++tot;
        ret=tree[ret].pos[tmp];
    }
    siz[ret]++;return;
}

priority_queue<pair<long long,pair<int,int> > >v;
inline long long query(long long x,int rk){
    int ret=1;long long ans=0;
    rep2(i,31,0){
        int tmp=(((1ll<<i)&x)!=0);
        if(!tree[ret].pos[tmp^1]){
            ret=tree[ret].pos[tmp];
        }else if(rk<=siz[tree[ret].pos[tmp^1]]){
            ret=tree[ret].pos[tmp^1];ans|=1ll<<i;
        }else{
            rk-=siz[tree[ret].pos[tmp^1]];ret=tree[ret].pos[tmp];
        }
    }
    return ans;
}

int main(){
    int n,k;read(n);read(k);
    rep(i,1,n){read(co[i]);pre[i]=(pre[i-1]^co[i]);}
    rep(i,0,n){change(pre[i]);}
    rep(i,0,n){v.push(make_pair(query(pre[i],1),make_pair(i,1)));}
    
    long long ans=0;
    rep(i,1,2*k){
        long long nop=v.top().first;ans+=nop;
        int nop1=v.top().second.first;int nop2=v.top().second.second;v.pop();
        if(nop2<n){
            v.push(make_pair(query(pre[nop1],nop2+1),make_pair(nop1,nop2+1)));
        }
    }
    write(ans/2);
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值