CF1101G (Zero XOR Subset)-less 线性基

传送门


既然每一次选择出来的都是一个子段,不难想到前缀和计算(然而我没有想到……)

设异或前缀和为\(x_i\),假设我们选出来的子段为\([1,i_1],(i_1,i_2],...,(i_{k-1},N]\),那么我们选择出来的子段的异或和为\(x_{i_1} , x_{i_2}\ xor\ x_{i_1},...,x_{i_{k-1}}\ xor\ x_N\)

又因为我们需要避免的是任意子段集合的异或和不为\(0\),那么将这些异或和互相异或对于这个命题是否成立不会产生影响。那么从第二项开始,每一项异或前面一项,就相当于我们选出来子段的异或和为\(x_{i_1},x_{i_2},...,x_{i_{k-1}} ,x_N\)

也就是说我们需要从前缀和中取出尽可能多的数,保证\(x_N\)在其中且它们线性无关。直接线性基扫一遍就可以得到答案。注意如果\(x_N=0\)则直接无解。

#include<bits/stdc++.h>
using namespace std;

inline int read(){
    int a = 0;
    char c = getchar();
    while(!isdigit(c))
        c = getchar();
    while(isdigit(c)){
        a = a * 10 + c - 48;
        c = getchar();
    }
    return a;
}

const int MAXN = 2e5 + 10;
int N , cnt , num[MAXN] , xxj[32];

int main(){
    N = read();
    for(int i = 1 ; i <= N ; ++i)
        num[i] = read() ^ num[i - 1];
    if(num[N] == 0){
        puts("-1");
        return 0;
    }
    xxj[(int)log2(num[N] + 0.5)] = num[N];
    cnt = 1;
    for(int i = 1 ; i < N ; ++i)
        while(num[i]){
            int t = (int)log2(num[i] + 0.5);
            if(xxj[t])
                num[i] ^= xxj[t];
            else{
                ++cnt;
                xxj[t] = num[i];
                break;
            }
        }
    cout << cnt << endl;
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Itst/p/10266032.html