题目地址:https://www.lydsy.com/JudgeOnline/problem.php?id=4260

题目:


给出一个序列,求两个不相交区间的异或值之和的最大值

 

解题思路:


异或基本性质:0^a=a,a^a=0

先求出前缀异或pre[]和后缀异或suf[]。

两个dp数组,dp1[i]表示[1,i]的最大区间异或值,dp2[i]表示[i,n]的最大区间异或值,dp1和dp2的求法相同,下面只介绍dp1的求法。

先初始化,将0插入01字典树中!!!因为0^a=0, 不能在一个空的01字典树上找一个数和指定的数异或值最大!!

dp1[i] =max(dp1[i -1], 以i为右边界的最大区间异或值) 即选择a[i]和选择a[i],01字典树中插入的都是pre值。

例如求dp1[4]时:

01字典树中已经插入和pre[1],pre[2],pre[3],若要选择a[4]的话,就要在pre[1],pre[2],pre[3]中选择一个和pre[4]的异或值最大,若pre[2]^pre[4]最大,相当于[1,4]中以4为右边界的最大区间异或值是a[3]^a[4],对应的区间是[3,4],在a[3]^a[4]和dp1[3]中取较大值更新dp1[4]。

最终结果ans = max(dp1[i],dp2[i + 1])i[1,n-1]

 

ac代码:


#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=4e5+5;
const int max_base = 32;
ll val[32*maxn], a[maxn], pre[maxn], suf[maxn], dp1[maxn], dp2[maxn];
int ch[32*maxn][2];
int tol = 0;
void init()
{
    tol = 1;
    ch[0][0] =  ch[1][0] = 0;
}
void insert(ll x)
{
    int u = 0;
    for(int i = max_base; i >= 0; i--)
    {
        int v = (x >> i & 1);
        if(!ch[u][v]) // 新建节点
        {
            ch[tol][1] = ch[tol][0] = 0;
            val[tol] = 0; //不是数值节点
            ch[u][v] = tol++;
        }
        u = ch[u][v];
    }
    val[u] = x;
}
ll query(ll x)
{
    int u = 0;
    for(int i = max_base; i >= 0; i--)
    {
        int v = x >> i & 1;
        if(ch[u][v^1]) u = ch[u][v^1]; // 贪心的找每位异或起来的1的节点, 且这个节点存在,可能其他数在该节点上有相应的值
        else u = ch[u][v];
    }
    return  x^val[u];
}
int main()
{
    //freopen("/Users/zhangkanqi/Desktop/11.txt","r",stdin);
    int  n;
    scanf("%d", &n);
    pre[0] = suf[n + 1] = 0;
    dp1[0] = dp2[n + 1] = 0;
    for(int i = 1; i <= n; i++)
    {
        scanf("%lld", &a[i]);
        pre[i] = pre[i - 1] ^ a[i];// 前缀异或
    }
    for(int i = n; i >= 1; i--)
        suf[i] = suf[i + 1] ^ a[i];// 后缀异或

    init();
    insert(0);//0^pre[1]=pre[1],01字典树在query之间必须曾经插入过数
    for(int i = 1; i <= n; i++)
    {
        dp1[i] =  max(dp1[i - 1], query(pre[i]));//dp[i] 表示前i个数内的最大区间异或值
        insert(pre[i]);
    }

    init();
    insert(0);
    for(int i = n; i >= 1; i--)
    {
        dp2[i] = max(dp2[i + 1], query(suf[i]));
        insert(suf[i]);
    }

    ll ans = 0;
    for(int i = 1; i < n; i++)
        ans = max(ans, dp1[i] + dp2[i + 1]);//[1,i] [i+1,n]
    printf("%lld\n", ans);
    return 0;
}