HDU_6579 19/暑期多校

前缀线性基

题目信息:

题目连接 10+(60×)
大意: 给一个数列,有两种操作 1. 1. 1. 在数列的末尾加入一个数 2. 2. 2. 给一个区间,询问区间内若干个数字的异或和最大值。强制在线。


思路:

若干个数的最大异或和,显然用线性基来做。但是这里是区间操作,就需要一种类似前缀和的东西来维护当前位的线性基。

考虑维护每个点的前缀线性基,线性基里将靠右的数字尽可能放高位,就是存一个额外存一个位置 p,表示这个位上的数的位置,从高位到低位扫,如果当前位置大于这个位上的位置那么交换,然后就得到了一个靠右的数字尽可能在高位的线性基

然后对于询问 [l,r]
在 r 的前缀线性基里找,只在位置大于等于 l 的位更新答案


代码

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
#define ll long long
const int maxn = 1e6+400;
int d[maxn][32],dt[maxn][32];
int mp[maxn],cnt;
int n,m;
void ad(int tt){
    for(int i=0;i<31;++i){
        d[tt][i] = d[tt-1][i];
        dt[tt][i] = dt[tt-1][i];
    }
    int x = mp[tt], p = tt;
    for(int i=30;i>=0;--i){
        if(x&(1<<i)){
            if(d[tt][i]){
                if(dt[tt][i]<p){
                    int t;
                    //碰到了比当前靠前的数交换
                    //试图让低位也变得更右
                    t=d[tt][i];d[tt][i]=x;x=t;
                    t=dt[tt][i];dt[tt][i]=p;p=t;
                }
                x ^= d[tt][i];
            }
            else{
                d[tt][i] = x;
                dt[tt][i] = p;
                break;
            }
        }
    }
}
int main(){
    int _;scanf("%d",&_);
    while(_--){
        scanf("%d%d",&n,&m);
        cnt = n;

        for(int i=1;i<=n;++i){
            scanf("%d",&mp[i]);
            ad(i);
        }
        int op,a,b;
        int L = 0;
        while(m--){
            scanf("%d",&op);
            if(op){
                scanf("%d",&a);
                a = a^L;
                mp[++cnt] = a;
                ad(cnt);
            }
            else{
                scanf("%d%d",&a,&b);
                a = (a^L)%cnt+1;
                b = (b^L)%cnt+1;
                if(a>b){int t=a;a=b;b=t;}

                int ans = 0;
                for(int i=30;i>=0;--i){
                    if(dt[b][i]<a) continue;
                    ans = max(ans,ans^d[b][i]);
                }
                printf("%d\n",ans);
                L = ans;
            }
        }
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值