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

原题: http://acm.hdu.edu.cn/contests/contest_showproblem.php?pid=1002&cid=848

题意:

给出n个数的数组,有两个操作

  1. [ l , r ] [l,r] [l,r]中选择一些数,使之异或最大,输出最大值;
  2. 在数组后面塞进去一个数。

解析:

选择一些数异或起来最大,那么就是线性基无疑了。不会线性基的同学可以看一下我的博客。

我们考虑维护每一个位置往左的31个基的位置。假设我已经知道了 i − 1 i-1 i1往左的31个基的位置和值。那么维护 i i i时,从高到低维护31个基。

  • 如果这个基之前没有出现过,则直接选择当前数作为这个位的基。
  • 如果出现过,则去比较位置。因为我们定下 R R R去查询时,基越靠右,越可能落在我的区间内。所以将靠右的作为基,将左边的值异或后,再往下更新。

代码:

#include
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define rrep(i,a,b) for(int i=a;i>=b;i--)
const int maxn=1e6+5;
int ji[maxn][31];
int pos[maxn][31];

void deal(int n,int val){
    int nex=n;
    rep(i,0,30)ji[n][i]=ji[n-1][i],pos[n][i]=pos[n-1][i];
    rrep(i,30,0){
        if(val&(1<<i)){
            if(!ji[n][i]){
                ji[n][i]=val;pos[n][i]=nex;return;
            }
            if(pos[n][i]<nex){
                swap(val,ji[n][i]);
                swap(nex,pos[n][i]);
            }
            val^=ji[n][i];
        }
    }
}

int main(){
    int t;scanf("%d",&t);
    while(t--){
        int lst=0;
        int n,m;scanf("%d%d",&n,&m);
        rep(i,1,n){
            int val;scanf("%d",&val);
            deal(i,val);
        }
        while(m--){
            int f;scanf("%d",&f);
            if(f==1){
                int val;scanf("%d",&val);
                val^=lst;
                deal(++n,val);
            }
            else{
                int l,r;scanf("%d%d",&l,&r);
                l^=lst,r^=lst;
                l%=n,r%=n;
                l++,r++;
                if(l>r)swap(l,r);

                int ans=0;
                rrep(i,30,0){
                    if(pos[r][i]>=l){
                        if((ans^ji[r][i])>ans){
                            ans^=ji[r][i];
                        }
                    }
                }
                printf("%d\n",ans);
                lst=ans;
            }
        }
    }
}

你可能感兴趣的:(数论/数学,例题)