最大异或和

最大异或和

题目描述

给定一个非负整数数列 a a a,初始长度为 N N N

请在所有长度不超过 M M M连续子数组中,找出子数组异或和的最大值。

子数组的异或和即为子数组中所有元素按位异或得到的结果。

注意:子数组可以为空。

输入格式

第一行包含两个整数 N N N, M M M

第二行包含 N N N个整数,其中第 i i i 个为 a i a_i ai

输出格式

输出可以得到的子数组异或和的最大值。

数据范围

对于 20 % 20\% 20% 的数据, 1 ≤ M ≤ N ≤ 100 1≤M≤N≤100 1MN100
对于 50 % 50\% 50% 的数据, 1 ≤ M ≤ N ≤ 1000 1≤M≤N≤1000 1MN1000
对于 100 % 100\% 100%的数据, 1 ≤ M ≤ N ≤ 1 0 5 1≤M≤N≤10^5 1MN105, 0 ≤ a i ≤ 2 31 − 1 0≤a_i≤2^{31}−1 0ai2311

输入样例:
3 2
1 2 4
输出样例:
6

思路

前缀和+trie+滑动窗口+贪心

  • 对于从区间 [ n , m ] [n,m] [n,m]的异或和可以表示为 s [ m ] ⨁ s [ n − 1 ] s[m] \bigoplus s[n-1] s[m]s[n1],可以先使用前缀和的思想,求出前缀异或和的结果

  • 贪心:对于每一个树要异或和最大,就需要高位不同,从高到低枚举每个数的每一位,在前缀和中查询是否存在不同的位,计算最大值

  • 滑动窗口,要求区间的长度不超过 m m m,如果长度超过 m m m,就删除前面添加到trie中的元素

实现代码

#include <iostream>
#include <cstdio>

using namespace std;

const int N = 30 * 100010;
// 每个数最多有30个点,
int cnt[N], idx;
int son[N][2], s[100010];
int n, m;
// 1表示插入,0表示删除
void insert(int x,int v)
{
    int p = 0;
    for(int i = 30;i >= 0;i --)
    {
        int u = x >> i & 1;
        if(!son[p][u]) son[p][u] = ++ idx;
        p = son[p][u];
        cnt[p] += v;
    }
}
// 查询
int query(int x)
{
    int p = 0;
    int res = 0;
    for(int i = 30;i >= 0;i --)
    {
        int u = x >> i & 1;
        if(cnt[son[p][!u]]) res = res * 2 + 1, p = son[p][!u];
        else res = res * 2, p = son[p][u];
    }
    return res;
}
int main()
{
    scanf("%d%d",&n,&m);   
    // 求异或和
    for(int i = 1;i <= n;i ++)
    {
        int x;
        scanf("%d",&x);
        s[i] = s[i -1] ^ x;
    }
    
    int res = 0;
    insert(0, 1);
    for(int i = 1;i <= n;i ++)
    {
        // 这里是删除i-m-1是因为 需要用的其实是比区间长度的位置多1
        if(i > m) insert(s[i - m - 1], -1);
        res = max(res,query(s[i]));
        insert(s[i],1);
    }
    printf("%d",res);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值