2019 Multi-University Training Contest 1 —— Operation(贪心+区间线性基)

2019HDU多校第一场B

题目链接

题意

给你一个序列,有两种操作:

1.求出区间[l,r]的异或最大值;

2.在序列后加一个x。

思路

求异或和的最大值,用线性基来做,关于线性基的定义和常用函数,见我的博客

此题用贪心+线性基来做,和传统的线性基相比,这里需要维护区间,以及数的位置,所以我们用两个二维数组来表示。v[i][j]代表第i个数为右边界时的线段基,j总是从高位开始更新。pos[i][j]代表数的位置。

详细解释见下列代码:

#include<bits/stdc++.h>
using namespace std;
int v[1000007][32],pos[1000007][32];//二维数组维护右边界,避免当后续数字输入时对前面已定边界的干扰,pos保存数的位置
void insert(int x,int w)
{
    int k=w;
    for(int i=30;i>=0;i--)//w从1开始增加,可以理解为先更新右区间为1的线段基;然后w取2时,先将现有的线段基复制给v[2][j],
    {        //然后在此基础上新增加一个数,更新v[2][j],这样就不会破坏v[1][j],以此类推,直到完成所有线段基的增加更新
        v[w][i]=v[w-1][i];
        pos[w][i]=pos[w-1][i];//每个数的位置也需要保留,因为数在线段基内位置会变,具体见后面
    }
    for(int i=30;i>=0;i--)
    {
        if(x>>i)//现将x的二进制右移i位,相当于x&(1<<i+1)
        {
            if(!v[w][i])//如果当前位置还没更新就更新
            {
                v[w][i]=x;
                pos[w][i]=k;
                return ;
            }
            else if(k>pos[w][i])//如果新输入的数b位置比原线段基此处数的位置大,那么交换,将原位置的数就行线段基下传处理
            {//(因为我们总是希望先找到比l位置大的位置的数,所以优先将位置大的数放后面)
                swap(v[w][i],x);
                swap(k,pos[w][i]);
            }
            x^=v[w][i];
        }
        
    }
}
int findmax(int l,int r)
{
    int ans=0;
    for(int i=30;i>=0;i--)//大的数据存在高位,所以找最大值从高位往低位找
    {
        if(pos[r][i]>=l&&(ans^v[r][i])>ans)
        {
            ans=ans^v[r][i];
        }
    }
    return ans;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        memset(v,0,sizeof(v));//多组输入,初始化数组
        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);
            insert(x,i);//当输入新数据时,要将数据传到insert函数,位置也需要记录
        }
        int op;
        int pre=0;//题目最后要求的,用于更新op操作
        while(m--)
        {
            scanf("%d",&op);
            if(op)
            {
                int x;
                scanf("%d",&x);
                x^=pre;
                insert(x,++n);
            }
            else
            {
                int l,r;
                scanf("%d%d",&l,&r);
                l=(l^pre)%n+1;
                r=(r^pre)%n+1;
                if(l>r)
                    swap(l,r);
                pre=findmax(l,r);
                printf("%d\n",pre);
            }
        }
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值