2019多校第一场 HDU6579 - Operation(线性基,在线求区间最大异或和)

本文介绍了一种使用线性基解决区间最大异或和问题的算法,通过构建前缀线性基和记录元素位置,实现高效查询与更新。文章详细解释了算法原理及其实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

链接: HDU 6579 - Operation

题意:

给出一段长度为n的序列 a[1] ~ a[n]

有以下操作:
0 l r :输出 a[l] ~ a[n] 中的最大异或和
1 x :把 x 加到序列尾,然后令 n = n +1

对于操作0:令 l=(l xor lastans) mod n + 1, r = (r xor lastans) mod n + 1, 若 l>r, 则 swap(l, r)
对于操作1:令 x=x xor lastans
其中 lastans 为上一次操作0输出的答案,其初值为0



分析:

用线性基求区间最大异或和,可以构建一个前缀线性基:d[r][i]

d[r][]表示一个 a[1] ~ a[r] 的线性基

那对于询问怎么保证左区间 l 呢,再设置一个 数组 pos[r][i],表示填入 d[r][i] 的数所在的位置

构建前缀线性基的方法:

主要是贪心思想:尽量把靠近 r 的数放在高位,这样无论 l 是多少,求得的最大异或和都能尽可能大(高位权值更大)。对于原本的数,应当继续和低位比较,择优存放。

因为后一个线性基就是前一个多插入了一个数,那么插入前应当先继承前一个线性基
令 d[r][i] = d[r-1][i],pos[r][i] = pos[r-1][i],其中1 < i < max_base

最后查询 l ~ r 的最大异或和时,只需要 查询线性基 d[r][],若 pos[r][i] >= l和d[r][i]异或可以令值更大,则和d[r][i]进行异或。



以下代码:

#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int maxn=1e6+10;
const int base=30;
int d[maxn][base+1],pos[maxn][base+1];
void ins(int x,int r,int p)
{
    for(int i=base;i>=0;i--)
    {
        if(x>>i&1)
        {
            if(!d[r][i])
            {
                d[r][i]=x;
                pos[r][i]=p;
                break;
            }
            else
            {
                if(p>pos[r][i])   //d[r][i]已有值,则更靠右的优先放在高位
                {
                    swap(d[r][i],x);   //原本的值继续和低位比较(不能直接舍弃)
                    swap(pos[r][i],p);
                }
                x^=d[r][i];
            }
        }
    }
}
int query_max(int l,int r)
{
    int ret=0;
    for(int i=base;i>=0;i--)
    {
        if(pos[r][i]>=l)   //要满足pos[r][i]>=l
            ret=max(ret,ret^d[r][i]);
    }
    return ret;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        memset(d,0,sizeof(d));
        memset(pos,0,sizeof(pos));
        int n,m;
        scanf("%d %d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            int x;
            scanf("%d",&x);
            for(int j=base;j>=0;j--)  //先继承前一个线性基
            {
                d[i][j]=d[i-1][j];
                pos[i][j]=pos[i-1][j];
            }
            ins(x,i,i);
        }
        int ans=0;
        while(m--)
        {
            int op;
            scanf("%d",&op);
            if(!op)
            {
                int l,r;
                scanf("%d %d",&l,&r);
                l=(l^ans)%n+1;
                r=(r^ans)%n+1;
                if(l>r)
                    swap(l,r);
                ans=query_max(l,r);
                printf("%d\n",ans);
            }
            else
            {
                int x;
                scanf("%d",&x);
                x^=ans;
                n++;
                for(int j=base;j>=0;j--)  //先继承前一个线性基
                {
                    d[n][j]=d[n-1][j];
                    pos[n][j]=pos[n-1][j];
                }
                ins(x,n,n);
            }
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值