在算法竞赛中,有一种特殊的处理方式针对一组数的异或运算。在 $mod\ 2$ 域内,任意一个维度仅有零向量与单位向量,而异或正是一种非常便利的在 $mod\ 2$ 域内的”高斯消元“。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
struct LBase {
    int dims = 0;
    vector<ll> d;
    bool zero = false;
    LBase(int n = 62) : dims(n) {
        d.resize(n);
    }
    bool insert(ll x) {
        for (int i = dims - 1; i >= 0; i --) {
            if (x & (1ll << i)) {
                if(d[i]) {
                    x ^= d[i];
                } else {
                    d[i] = x;
                    /* 下面的代码需要用到最小构造时加入
                    for(int j = i - 1; j >= 0; j --) {
                        if(d[j] && (d[i] >> j & 1) ) d[i] ^= d[j];
                    }
                    for(int j = dims - 1; j > i; j --) {
                        if(d[j] >> i & 1) d[j] ^= d[i];
                    }
                    ------------------------------ */
                    break;
                }
            }
        }
        zero |= x == 0;
        return x > 0;
    }
    ll get_max() {
        ll x = 0;
        for (int i = dims - 1; i >= 0; i --) if(d[i]) {
            x = max(x, x ^ d[i]);
        }
        return x;
    }
    ll get_min() {
        ll x = 0;
        for(int i = 0; i < dims; i ++) if(d[i]) {
            x = d[i];
            break;
        }
        return x;
    }
    bool check_ex(ll x) {
        for(int i = dims - 1; i >= 0; i --) {
            if(x & (1 << i)) {
                if(!d[i])  {
                    return false;
                }
                x ^= d[i];
            }
        }
        return true;
    }
    void merge(const LBase &a) {
        for (int i = a.dims - 1; i >= 0; i --) if(a.d[i]) {
            insert(a.d[i]);
        }
    }
    int get_ranks() {
        int ranks = 0;
        for(int i = 0; i < dims; i ++) {
            ranks += (d[i] > 0);
        }
        return ranks;
    }
    // 求第 k 小, 需要用到最小构造
    ll kth(ll k) {
        ll res = 0, rks = get_ranks();
        if(zero) k --;
        if(k >= (1 << rks)) return -1;
        for(int i = 0; i < dims; i ++) {
            if(k & (1ll << i)) res ^= d[i];
        }
        return res;
    }
};

2023 牛客多校 9 G

Non-Puzzle: Game

题目大意

A,B 两人博弈,初始有 $n$ 个数,$a_1, a_2, …, a_n$,A 先手,每人可以任意选择两个数 $a_i,a_j$($1\leq i,j\leq n$),并将 $a_i \bigoplus a_j$ 加入原数组中,先得到 $k$ 的人获胜。

题解

事实上,如果 A 第一回合无法获胜,那么就只有平局和 B 胜的情况。

那么 B 胜的情况事实上非常好考虑,即

$$ \forall i, j, t, a_i \bigoplus a_j \bigoplus a_t = k $$

但是题目所给的 $n$ 非常大,显然上式不能用常规的方法证明,需要另寻他法。

通过对上式进行一些转化,可以写出:

$$ \forall i, j, t, (a_i \bigoplus k) \bigoplus (a_j \bigoplus k) = a_t\bigoplus k $$

可以定义一种新的运算 $F$ 来定义上式,即:

$$ a_i F a_j = a_t $$

原题就转化成了代数系统 $(a, F)$ 是闭包的。

最后根据异或线性基的性质,仅需证明由 $a_i \bigoplus k$ 构成的集合的秩的二次幂等于集合大小。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
struct LBase {
    int dims = 0;
    vector<ll> d;
    LBase(int n = 62) : dims(n) {
        d.resize(n);
    }
    bool insert(ll x) {
        for (int i = dims - 1; i >= 0; i --)
            if (x & ((ll)1 << i)) {
                if(d[i]) {
                    x ^= d[i];
                } else {
                    d[i] = x;
                    break;
                }
            }
        return x > 0;
    }
    int get_ranks() {
        int ranks = 0;
        for(int i = 0; i < dims; i ++) {
            ranks += (d[i] > 0);
        }
        return ranks;
    }
};
void solve() {
    int n, k; 
    cin >> n >> k;
    set<int> s;
    for(int i = 0; i < n; i ++) {
        int x;
        cin >> x;
        s.insert(x);
    }
    for(int i : s) {
        if(s.find(i ^ k) != s.end()) {
            cout << "Alice\n";
            return;
        }
    }
    LBase lb;
    int cnt = 0;
    set<int> T;
    for(int x : s) {
        lb.insert(x ^ k);
        T.insert(x ^ k);
    }
    int r = lb.get_ranks();
    if(((1ll << r) == T.size())) {
        cout << "Bob\n";
    } else {
        cout << "Draw\n";
    }
}