[AGC045] A - Xor Battle

[AGC045] A - Xor Battle

知识点 线性基

参考博客:
Menci 线性基学习笔记
fffasttime 异或和与线性基问题入门

总体看下来感觉知识点不算难,但是得多看不同的博客才能找到自己看懂这个知识点的节奏…

过于严谨的数学定义不予深究,毕竟也只是在网络博客的只言碎语中学习到了该知识点,并未进行严谨的系统学习。简单的说,如果给出某个序列,我们可以据此得到这个序列所蕴含的基底,通过这组基底,可以张成一个至少包含该序列中所有值的空间。因而,如果我们需要对原序列的进行某些异或操作,就有可能可以等价地通过其线性基来操作。

构造办法如下:

const int L = 63;
ll d[L];
bool add(ll x){
    for(int i = L - 1; i >= 0; i--) if(x & 1ll << i){
        if(d[i] == -1){d[i] = x; return true;}
        x ^= d[i];
    }
    return false;
}

如何获得基底的思想与Gauss消元类似,在矩阵中不断相减得到最简三角矩阵。

在这道题中,我们设置集合 S [ i ] S[i] S[i],包含了第i次操作前能够使得最终结果为0的所有元素。从后往前遍历,有如下关系:

  • s [ i ] = 0 s[i] = 0 s[i]=0,那么 S [ i ] = S [ i + 1 ] ∪ { x ∣ x = a [ i ] ⊕ u , u ∈ S [ i + 1 ] } S[i] = S[i + 1] ∪\{x|x = a[i] ⊕ u, u ∈S[i + 1]\} S[i]=S[i+1]{xx=a[i]u,uS[i+1]}
  • s [ i ] = 1 s[i] = 1 s[i]=1:若 a [ i ] ∈ S [ i + 1 ] a[i]∈S[i + 1] a[i]S[i+1]:记 x ∈ S [ i ] x∈S[i] xS[i] ,那么必有 x ⊕ a [ i ] ∈ S [ i + 1 ] x ⊕ a[i] ∈S[i + 1] xa[i]S[i+1],只有这样才能保证无论1号怎么选择最终都会失败,从而由集合 S S S封闭性易知 S [ i ] = S [ i + 1 ] S[i] = S[i+ 1] S[i]=S[i+1];当 a [ i ] ∉ S [ i + 1 ] a[i]∉S[i + 1] a[i]/S[i+1]时,进行i操作之前的结果 x x x x ⊕ a [ i ] x⊕a[i] xa[i]总有一个不在 S [ i + 1 ] S[i + 1] S[i+1]中,此时1必胜。

因为集合的封闭性,所以张成的向量空间里所有的值都可以被取到。综上考虑,我们可以用线性基维护该集合。(然而这一块感觉还是有点不太清楚qwq

const int maxn = 200 + 10;
ll a[maxn];

const int L = 63;
ll d[L];
bool add(ll x){
    for(int i = L - 1; i >= 0; i--) if(x & 1ll << i){
        if(d[i] == -1){d[i] = x; return true;}
        x ^= d[i];
    }
    return false;
}

int main(){
//    Fast;
    int t; scanf("%d", &t); while(t--){
        memset(d, -1, sizeof d);
        
        int n; scanf("%d", &n);
        for(int i = 0; i < n; i++) scanf("%lld", a + i);
        char s[maxn]; scanf("%s", s);
        int flag = 0;
        for(int i = n - 1; i >= 0; i--){
            if(add(a[i]) && s[i] == '1'){
                flag = 1;
                break;
            }
        }
        printf("%d\n", flag);
    }
    return 0;
}

你可能感兴趣的:(ACM)