2019杭电多校训练第二场1002

https://vjudge.net/problem/HDU-6579

 

题意:

给出n个数,接下来有m次操作。

1   k:在数组后面多加一个k。(n=n+1)。

0  l   r:询问[l,r]区间内任意取数,异或和的最大值。

特别要注意的是2种操作都加密了,1号操作需要k=k^lasten;2号操作需要l=(l^lasten)%n+1,r=(r^lasten)%n+1.lasten初始值为0,以后lasten为上一次0号操作的答案。

 

思路:

前缀线性基。建立二维数组pre_p[1000009][maxbit],表示从1~i前i个数组成的线性基。建立数组pre_pos[1000009][maxbit],表示对于第i个前缀线性基第j位为1的数的最大下标(另一种理解: 对于区间[1,i]内的数,二进制第j位为1的最大下标。可能是由不止一个数异或而成的,此时记录这几个数的最小下标)。

对于每一次1号操作,都要在重新维护pre_p数组和pre_pos数组。

对于0号操作,我们按普通的线性基用法使用pre_p[r],不同的就是对于每一个不为0的pre_p[r][j]都要判断pre_pos[r][j]>=l是否成立。即当右端点固定为r时,pre[r][j]是否可以在l以后出现。

(本人原本maxbit开的是32,然后一直血wa,换成31就过了,血的教训)

 

#include<bits/stdc++.h>
using namespace std;
const int maxbit=31;
#define met(a,b) memset(a,b,sizeof(a))
int p[maxbit+5],pos[maxbit+5],pre_p[1000009][maxbit],pre_pos[1000009][maxbit];
void solve(int val,int adr )//建立1~adr的前缀线性基
{
    int k=adr;
    for(int i=maxbit-1; i>=0; i--)
    {
        if((val>>i)&1)//
        {
            if(!p[i])
            {
               // cout<<(val>>i)<<endl;
               // cout<<i<<"__________ "<<p[i]<<"....."<<val<<endl;
                p[i]=val;
                pos[i]=adr;
                break;
             }
            else
            {
                if(pos[i]<adr)swap(p[i],val),swap(adr,pos[i]);//本题重点代码,更新二进制第i为1的下标
                val^=p[i];//新的val是原val与原p[i]异或得到的,所以adr应该是2者较小的下标(即原p[i]的下标),所以上面用swap而不是赋值。
            }
        }
    }
    for(int i=0; i<=maxbit-1; ++i)
    {
        pre_p[k][i]=p[i];
        pre_pos[k][i]=pos[i];
    }
}
int seek_ans(int l,int r)//在区间[l,r]内搜寻答案
{
    if(l>r) swap(l,r);
    int k=0;
    for(int i=maxbit-1; i>=0; i--)
    {
        if(pre_p[r][i] &&pre_pos[r][i]>=l)
            k=max(k,k^pre_p[r][i]);
    }
    return k;
}
int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);
    int T;
    cin>>T;
    while(T--)
    {
        int n,m,k;
        cin>>n>>m;
        for(int i=1; i<=n; i++)
        {
            cin>>k;
            solve(k,i);
        }
        int lastans=0;
        for(int t=0; t<m; t++)
        {
            int f;
            cin>>f;
            if(f)
            {
                cin>>k;
                k^=lastans;//解密
                solve(k,++n);//建立线性基
            }
            else
            {
                int l,r;
                cin>>l>>r;
                lastans=seek_ans((l^lastans)%n+1,(r^lastans)%n+1);//解密找答案
                cout<<lastans<<endl;
            }
        }
        for(int j=maxbit-1;j>=0;--j) p[j]=pos[j]=0;//初始化
        for(int i=0;i<=n;i++)
        {
            for(int j=maxbit-1;j>=0;--j)
                pre_p[i][j]=pre_pos[i][j]=0;
        }
    }
    return 0;
}

  

posted @ 2019-07-26 17:19  一只球球  阅读(198)  评论(1编辑  收藏  举报