奶牛异或(字典树求异或极值)

原题链接
题意:给你n个数求一段区间[l,r]使得该区间的异或和最大,若有多个,求区间右端点最小的那个,如果还有多个,求区间长度最段的那个

思路:先求一个异或前缀和,假设最后答案是[x,y]区间,那么就是[1,x]区间的异或和 与 [1,y]区间的异或和 异或的结果,用为每一个前缀和构造0-1字典树(从高位到低位),然后枚举前缀和pre[i] 在字典树上查询。

同样的,也可以求树上的一条路径(u,v)间的异或极值
在这里插入图片描述
代码:

//#pragma GCC optimize("Ofast")
//#pragma GCC target("avx,avx2,fma")
//#pragma GCC optimization ("unroll-loops")
#include <bits/stdc++.h>
#define pb push_back
#define ll long long
#define IOS std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;

int n,a[100005];
int pre[100005],val;
int p[100];
map<int,int> Map;
int tree[3500000][3],k=1;
void insertt()
{
    int os=0;
    for(int i=0;i<22;i++)
    {
        int c=p[i];
        if(tree[os][c]==0)
        {
            tree[os][c]=k;
            k++;
        }
        os=tree[os][c];
    }
}
void query(ll x)
{
    int os=0;
    for(int i=21;i>=0;i--)
    {
        int c=(x>>i)&1;
        if(c==0)
        {
            if(tree[os][1]!=0)
            {val+=(1<<i);
            os=tree[os][1];}
            else
            {os=tree[os][0];}
        }
        else
        {
            if(tree[os][0]!=0)
            {os=tree[os][0];}
            else
            {val+=(1<<i);
            os=tree[os][1];}
        }
    }
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {cin>>a[i];}
    if(n==1)
    {
        cout<<a[1]<<" 1 1"<<endl;
        return 0;
    }
    for(int i=1;i<=n;i++)
    {
        pre[i]=pre[i-1]^a[i];
        if(Map[pre[i]]==0)
        {Map[pre[i]]=i;}
        int op=pre[i],cnt=0;
        for(int j=21;j>=0;j--)
        {
            p[cnt++]=(op>>j)&1;
        }
        insertt();
    }
    int maxx=0,px=0,py=0,maxp,minp;
    for(int i=1;i<=n;i++)
    {
        val=0;
        query(pre[i]);
        if(val==0){maxp=i;minp=i;}
        else{maxp=max(Map[val],i);minp=min(Map[val],i);}
        if((val^pre[i])>maxx)
        {
            maxx=val^pre[i];
            px=minp;
            py=maxp;
        }
        if((val^pre[i])==maxx)
        {
            if(maxp<py)
            {
                px=minp;
                py=maxp;
            }
            if(maxp==py)
            {
                if(minp>px)
                {
                    px=minp;
                    py=maxp;
                }
            }
        }
    }
    if(px==py)
    {cout<<maxx<<" "<<px<<" "<<py<<endl;}
    else
    {cout<<maxx<<" "<<px+1<<" "<<py<<endl;}
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值