HDU 6579 Operation(前缀线性基)

本文介绍了一种解决区间异或最大值问题的高效算法——前缀线性基。通过实例讲解了如何使用前缀线性基进行区间异或最大值的查询和元素插入操作,详细解释了算法的实现细节,并提供了完整的代码示例。
大致题意

给长度为n的数列,m次操作(n<=5e5,m<=5e5,ai<(1<<30))
操作0:给x,y,查询区间[x,y]中选若干数异或和的最大值。
操作1:在n+1的位置添加数字x,数组长度n=n+1;
强制在线。

思路

打重现的时候,一看区间异或最大值,复杂度也没算,直接写了个假算法:线段树维护区间线性基…,后来优化了半天一直TLE,后来写写复杂度发现是30logn(n+m)的…giaogiao
后来想了想有没有前缀和线性基啊…然后就没想出来
结果还真有…主要还是对线性基的理解不够深刻。

前缀线性基,开两个数组,ji[maxn][30],pos[maxn][30],一个存前i个的线性基,另一个存每个线性基是来自哪个下标。建立线性基的标准就是,从到小,每一位都用尽量靠右的数去当基。然后查询的时候只要多判断当前基来自的下标是否大于等于l。

这个题看似有插入,实际上其实算是静态的。所以静态的区间异或最大值都可以用前缀线性基来做。

代码
#include<bits/stdc++.h>
using namespace std;
#define maxn 1000005
#define maxm 1000006
#define ll long long int
#define INF 0x3f3f3f3f
#define inc(i,l,r) for(int i=l;i<=r;i++)
#define dec(i,r,l) for(int i=r;i>=l;i--)
#define mem(a) memset(a,0,sizeof(a))
#define sqr(x) (x*x)
#define inf (ll)2e18+1
int read(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
     while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
     return f*x;
}
int t,n,m,ans,a[maxn];
int ji[maxn][30],pos[maxn][30];
void ins(int x,int id)
{
    int now=id;
    inc(i,0,29){
        ji[id][i]=ji[id-1][i];
        pos[id][i]=pos[id-1][i];
    }
    dec(i,29,0){
        if(x&(1<<i)){
            if(!ji[id][i]){
                ji[id][i]=x;
                pos[id][i]=now;
                break;
            }
            if(pos[id][i]<now){
                swap(ji[id][i],x);
                swap(pos[id][i],now);
            }
            x^=ji[id][i];
        }
    }
}
int query(int l,int r)
{
    int res=0;
    dec(i,29,0){
        if(pos[r][i]>=l){
            res=max(res,res^ji[r][i]);
        }
    }
    return res;
}
int main()
{
    t=read();
    while(t--){
        mem(ji);mem(pos);
        n=read();m=read();
        inc(i,1,n)ins(read(),i);
        ans=0;
        inc(i,1,m){
            int cmd,x,y;
            cmd=read();
            if(cmd==0){
                x=read();y=read();
                x^=ans;y^=ans;
                x%=n;y%=n;
                x++;y++;
                if(x>y)swap(x,y);
                ans=query(x,y);
                printf("%d\n",ans);
            }
            else {
                x=read();
                x^=ans;
                ins(x,++n);
            }
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值