2019杭电多校第一场 B - Operation(线性基+贪心)

题意:

给出一个长度为 n n n数组, m m m 次操作,操作分为两种:

1. 1. 1. 查询区间 [ l , r ] [l,r] [l,r] 的一些数异或和的最大值。

2. 2. 2. 向数组最后添加一个数 x x x, n = n + 1 n=n+1 n=n+1

题解:

先了解什么是线性基

线性基就是从一个集合中构造出另一个集合,且满足一下要求

1. 1. 1. 线性基的元素能互相异或得到原集合异或出来的数

2. 2. 2. 线性基满足 1 1 1的条件下,集合元素最少

3. 3. 3. 线性基没有异或和为 0 的子集。

4. 4. 4. 线性基中每个元素的异或方案唯一,也就是说,线性基中不同的异或组合异或出的数都是不一样的。

5. 5. 5. 线性基中每个元素的二进制最高位互不相同。

线性基构造方法:


inline void insert(long long x) {
    
    
  for (int i = 55; i + 1; i--) {
    
    
    if (!(x >> i))  // x的第i位是0
      continue;
    if (!p[i]) {
    
    
      p[i] = x;
      break;
    }
    x ^= p[i];
  }
}

再来看这道题:

d p [ i ] [ j ] dp[i][j] dp[i][j]表示前 i 个数的线性基, p o s [ i ] [ j ] pos[i][j] pos[i][j] 表示前 i 个数的线性基 j j j 位的位置

那么在插入新的数 X X X 时,若 d p [ i ] [ j ] dp[i][j] dp[i][j] 有值,且 i > p o s [ i ] [ j ] i>pos[i][j] i>pos[i][j] ,那么就把 X X X d p [ i ] [ [ j ] dp[i][[j] dp[i][[j]交换 ,保证 线性基里的数都是尽量向右靠的。那么在查询的过程中,只需判断位置是否 ≥ l \geq l l 即可。

代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<stack>
#include<set>
#include<ctime>
#define iss ios::sync_with_stdio(false)
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef pair<int,int>pii;
const int MAXN=5e5+5;
const int mod=1e9+7;
const int inf=0x3f3f3f3f;
int a[MAXN];
int dp[MAXN][31];
int pos[MAXN][31];
void add(int id,int x){
    
    
    int temp=id;
    for(int i=0;i<=30;i++){
    
    
        dp[id][i]=dp[id-1][i];
        pos[id][i]=pos[id-1][i];
    }
    for(int i=30;i>=0;i--){
    
    
        if((x>>i)&1){
    
    
            if(!dp[id][i]){
    
    
                dp[id][i]=x;
                pos[id][i]=temp;
                break;
            }
            else{
    
    
                if(temp>pos[id][i]){
    
    
                    swap(x,dp[id][i]);
                    swap(temp,pos[id][i]);
                }
                x=(x^dp[id][i]);
            }
        }
    }
}   
int solve(int l,int r){
    
    
    int ans=0;
    for(int i=30;i>=0;i--){
    
    
        if(pos[r][i]>=l){
    
    
            if((ans^dp[r][i])>ans){
    
    
                ans=(ans^dp[r][i]);
            }
        }
    }
    return ans;
}
int main(){
    
    
    int t;
    scanf("%d",&t);
    while(t--){
    
    
        for(int i=0;i<=30;i++){
    
    
            dp[0][i]=pos[0][i]=0;
        }
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
    
    
            cin>>a[i];
            add(i,a[i]);
        }
        int ans=0;
        while(m--){
    
    
            int op;
            scanf("%d",&op);
            if(op==0){
    
    
                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=solve(l,r);
                printf("%d\n",ans);
            }
            else{
    
    
                int x;
                scanf("%d",&x);
                x=x^ans;
                n++;
                add(n,x);
            }
        }
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_45755679/article/details/114902782