高斯消元法异或版

文章目录

高斯消元法异或版

  • 将加法方程组改为异或方程组,比如说:
aiignore 复制代码
求解以下包含 4 个变量的异或线性方程组:
1. x1 ∧ x2 ∧ x3 = 0
2. x1 ∧ x3 ∧ x4 = 1
3. x2 ∧ x3 ∧ x4 = 1
4. x3 ∧ x4 = 0
可以计算的结果为:
可以通过代入消元法快速得出结果:
* 由方程 4 可得:x3 = x4。
* 代入方程 2:x1 ∧ x3 ∧ x3 = x1 ∧ 0 = 1,解得 x1 = 1。
* 代入方程 3:x2 ∧ x3 ∧ x3 = x2 ∧ 0 = 1,解得 x2 = 1。
* 代入方程 1:1 ∧ 1 ∧ x3 = 0,解得 x3 = 0,由于 x3 = x4,故 x4 = 0。
最终解为:
* x1 = 1
* x2 = 1
* x3 = 0
* x4 = 0
其实也可以换为高斯方程组的形式,他们的系数为:
       1   2   3   4   5
    ------------------------------------------------------------
  1 |  1   1   1   0   0
  2 |  1   0   1   1   1       
  3 |  0   1   1   1   1
  4 |  0   0   1   1   0
对于这里的系数可以对他们进行和加法一样的高斯消元,可以得到:
       1   2   3   4   5
    ------------------------------------------------------------  
  1 |  1   0   0   0   1
  2 |  0   1   0   0   1 ===>主元(及对角线)都为1,说明有唯一解,就也可以得到以上结果
  3 |  0   0   1   0   0
  4 |  0   0   0   1   0
  • 依据以上,可以写出模板代码:
算法复杂度分析

时间复杂度:O(n^3)

异或高斯消元的核心是三层嵌套循环:

  • 外层循环遍历 n 个变量(主元);
  • 中层循环遍历 n 行,寻找当前列的主元;
  • 内层循环从第 i 列一直异或到第 n+1 列,逐列消元。

因此总操作次数约为 n × n × n = n^3,即时间复杂度为 O(n^3)

空间复杂度:O(n^2)

模板代码使用二维数组 graph 存储增广矩阵,大小为 (n+1) × (n+2),因此空间复杂度为 O(n^2)

为什么 n=1000 时需要 bitset 优化?

当 n=1000 时,朴素 O(n^3) 算法需要执行约 10^9 次异或操作,在竞赛环境下很容易超时。

bitset 的优化思路是:把矩阵的每一行看成一个长度为 n 的 0/1 位串,用 C++ 标准库的 bitset 存储。这样,原本需要循环 n 次才能完成的一整行异或操作,现在只需一条 CPU 指令即可完成(row1 ^= row2),速度提升约 32 倍甚至 64 倍,从而把 10^9 次操作压缩到可接受的范围。

这也是题目三(洛谷 P2447 外星千足虫)中 N 最大为 1000、M 最大为 2000 时,必须使用 bitset 加速的根本原因。

下面用一张 ASCII 示意图直观对比普通二维数组逐位异或与 bitset 整行异或的差异:

text 复制代码
普通二维数组:逐位异或(需要 n 次循环)
┌─────────────────────────────────────────────────────────────┐
│  row1:  1  0  1  1  0  1  0  1  ...  1  0  1  1  0  1  0  1 │
│         ⊕  ⊕  ⊕  ⊕  ⊕  ⊕  ⊕  ⊕  ⊕  ⊕  ⊕  ⊕  ⊕  ⊕  ⊕  ⊕ │
│  row2:  0  1  1  0  1  0  1  0  ...  0  1  1  0  1  0  1  0 │
│         ↓  ↓  ↓  ↓  ↓  ↓  ↓  ↓       ↓  ↓  ↓  ↓  ↓  ↓  ↓  ↓ │
│  result:1  1  0  1  1  1  1  1  ...  1  1  0  1  1  1  1  1 │
│         ↑  ↑  ↑  ↑  ↑  ↑  ↑  ↑       ↑  ↑  ↑  ↑  ↑  ↑  ↑  ↑ │
│    第1位 第2位 第3位 第4位 ... 第n位(每个位置都要单独循环一次)│
└─────────────────────────────────────────────────────────────┘
        循环次数 = n 次(n = 1000 时,一行就要循环 1000 次)

bitset:整行异或(只需 1 条指令)
┌─────────────────────────────────────────────────────────────┐
│  row1:  10110101...10110101  (一整行 0/1 串,打包成一个整数)│
│          ⊕                                                 │
│  row2:  01101010...01101010  (一整行 0/1 串,打包成一个整数)│
│          ↓                                                  │
│  result:11011111...11011111  (一次异或,整行同时完成)       │
│          ↑                                                  │
│       一条 CPU 指令(row1 ^= row2),n 位同时参与运算         │
└─────────────────────────────────────────────────────────────┘
        循环次数 = 1 次(n = 1000 时,一行只需 1 条指令)

加速倍数 ≈ 32 倍(32 位机器)~ 64 倍(64 位机器)

从图中可以直观看到:普通二维数组需要逐位循环 n 次才能完成一行异或,而 bitset 把整行 0/1 串打包成一个整数,一条指令即可完成全部 n 位的异或运算,这正是 n=1000 时性能提升 32~64 倍的关键所在。

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
static int MAXN = 101;
static vector<vector<int>> graph;
// 高斯消元解决异或方程组模板
static void guass(int n) {
    // i 代表当前正在处理哪一列(哪个变量),也是当前想要确定主元的行
    for (int i = 1; i <= n; i++) {
        // j 是用来寻找主元的行指针
        for (int j = 1; j <= n; j++) {
            // 核心技巧:如果 j 在 i 的上面(即 j < i),并且那一行已经被处理成对角矩阵了(mat[j][j]==1),
            // 说明这行已经被用过了,直接跳过,继续往下找可以作为主元的行。
            if (j < i && graph[j][j] == 1) {
                continue;
            }
            // 找到当前列(第 i 列)为 1 的行,将这一行交换到当前第 i 行作为主元行
            if (graph[j][i] == 1) {
                swap(graph[i], graph[j]);
                break; // 交换完主元后,跳出找主元的循环
            }
        }
        // 如果第 i 行第 i 列是 1,说明找到了有效主元,开始消元
        if (graph[i][i] == 1) {
            // 遍历所有行,把其他行第 i 列的元素消成 0
            for (int j = 1; j <= n; j++) {
                // 跳过自己那一行,只要其他行第 i 列有 1,就用异或运算消掉它
                if (i != j && graph[j][i] == 1) {
                    // 从第 i 列开始一直异或到最后一列(n+1列为常数项)
                    // 前面的列都是0,异或也没意义,所以 k 从 i 开始即可
                    for (int k = i; k <= n + 1; k++) {
                        graph[j][k] ^= graph[i][k];
                    }
                }
            }
        }
    }
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n;
    cin >> n; // 输入变量个数(也是方程个数)
    // 开辟 n+1 行,n+2 列的二维数组(下标从1开始,最后一列 n+1 存常数项)
    graph.assign(n + 1, vector<int>(n + 2, 0));
    // 读入增广矩阵:前 n 列是系数,第 n+1 列是等号右边的常数
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n + 1; j++) {
            cin >> graph[i][j];
        }
    }
    // 调用高斯消元函数进行消元
    guass(n);  
    // 输出消元后的矩阵,此时最后一列就是方程组的解
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n + 1; j++) {
            cout << graph[i][j] << " ";
        }
        cout << endl;
    }
    return 0;
}

看几个好题

高斯消元法求解完美平方数问题

问题描述

朱和 772002 都擅长数学。有一天,朱想测试 772002 的能力,于是请 772002 解一道数学题。

但 772002 和他的女朋友有个约会。所以 772002 把这个问题交给了你。

有个数字 a1, a2, ..., an。每个数字的质数值不超过 2000,你可以选一个数字并将其相乘,然后得到一个数字 b。

有多少种不同的选择方式可以使 b 成为一个完美平方数。答案可能太大了,所以你应该以模 1000000007 来输出。

输入

第一行是一个正整数 T,表示存在 T 个测试用例。

对于每个测试用例:

第一行包含一个数字 n(1 <= n <= 300),下一行有 n 个数字 a1, a2, ..., an(1 <= ai <= 10^18)。

输出

对于第 i 个测试用例,首次输出 Case #i: 在单行中。

然后将第 i 个测试用例的答案模除 1000000007。

采样输入
in 复制代码
2
3
3 3 4
3
2 2 2
采样输出
out 复制代码
Case #1:
3
Case #2:
3
解题思路

1. 将"乘法"转化为"异或"

一个数如果是完美平方数,那么它的所有质因数的指数必须是偶数。

例如:12 = 2^2 * 3^1,指数分别是 2(偶)和 3(奇)。如果要让它变成完全平方数,就必须再乘一个含有质因数3的数。

在模 2(异或)的世界里,我们只关心质因数的指数是奇数还是偶数。如果是奇数记为 1,偶数记为 0。

2. 构造 01 向量

因为 ai <= 10^18,且题目保证质因数不超过 2000。2000 以内的质数总共只有 303 个。

对于输入的每一个数字 ai,我们把它分解质因数,然后构造出一个只包含 0 和 1 的向量(长度为 303)。例如数字 3,它的向量就是 0, 1, 0, 0...(只有代表质数 3 的位置是 1)。

这 303 个质数就是方程的"变量"。

3. 建立异或方程组并消元

我们要从 n 个数中选出一部分相乘,相当于把这 n 个01向量进行异或操作。

要让乘积为完全平方数,就是要让选出的所有向量异或之后,结果为全0。

所以,我们把每个数字的01向量当成方程的一行,构成一个 n 行、303列的矩阵,然后直接套用左神的异或高斯消元法(guass)来化简矩阵,求出矩阵的秩(Rank)。

4. 计算最终答案

通过高斯消元,我们可以得到矩阵的秩 r。

一共有 n 个数,其中有 r 个是"关键变量"(主元),剩下的 n - r 个就是"自由变量"。

自由变量可以任意选或不选(有 2^(n-r) 种选法)。

一旦自由变量确定,主元的选择是唯一的(为了抵消掉自由变量产生的异或结果,让最终结果为全0)。

题目要求"不同的选择方式",且不能一个都不选(不选不构成有效的 b),所以答案就是:

2^(n-r) - 1

最后用快速幂计算 2^(n-r),再减1,并取模 1000000007 即可。

代码如下:
cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
static int MAXN = 305; // 题目要求 n <= 300
static vector<vector<int>> graph;
// 高斯消元解决异或方程组模板(完全保留原版变量名)
static void guass(int n) {
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            if (j < i && graph[j][j] == 1) {
                continue;
            }
            if (graph[j][i] == 1) {
                swap(graph[i], graph[j]);
                break;
            }
        }
        if (graph[i][i] == 1) {
            for (int j = 1; j <= n; j++) {
                if (i != j && graph[j][i] == 1) {
                    for (int k = i; k <= n + 1; k++) {
                        graph[j][k] ^= graph[i][k];
                    }
                }
            }
        }
    }
}
vector<int> primes;
bool is_composite[2005];
void init_primes() {
    for (int i = 2; i <= 2000; i++) {
        if (!is_composite[i]) {
            primes.push_back(i);
            if ((long long)i * i <= 2000) {
                for (int j = i * i; j <= 2000; j += i) {
                    is_composite[j] = true;
                }
            }
        }
    }
}
long long quick_pow(long long a, long long b, long long mod) {
    long long res = 1;
    while (b) {
        if (b & 1) res = res * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return res;
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    init_primes();
    int T;
    cin >> T;
    for (int t = 1; t <= T; t++) {
        int n;
        cin >> n;
        // 开辟 n+1 行,n+2 列的二维数组(下标从1开始)
        graph.assign(n + 1, vector<int>(n + 2, 0));
        for (int i = 1; i <= n; i++) {
            long long a;
            cin >> a;
            for (int p = 0; p < primes.size(); p++) {
                int cnt = 0;
                while (a % primes[p] == 0) {
                    cnt ^= 1; 
                    a /= primes[p];
                }
                if (cnt == 1) {
                    // 因为 guass(n) 只处理前 n 列,所以质数编号小于 n 的填入系数列
                    if (p < n) {
                        graph[i][p + 1] = 1; 
                    } else {
                        // 超过前 n 列的质数,强行算作常数项(放在 n+1 列)进行处理
                        graph[i][n + 1] ^= 1; 
                    }
                }
            }
        }
        guass(n);
        int rank = 0; 
        for (int i = 1; i <= n; i++) {
            bool all_zero = true;
            for (int j = 1; j <= n + 1; j++) {
                if (graph[i][j] == 1) {
                    all_zero = false;
                    break;
                }
            }
            if (!all_zero) {
                rank++;
            }
        }
        int free_vars = n - rank;
        long long ans = (quick_pow(2, free_vars, 1000000007) - 1 + 1000000007) % 1000000007;
        cout << "Case #" << t << ":\n" << ans << "\n";
    }
    return 0;
}

思考:

aiignore 复制代码
首先我们根据题意可以假想出这样一个一共有n个点,m条无向边,每个点的初始状态都是0
根据题意可以操作任意一个点,操作后该点以及相邻点的状态都会改变
最终是希望所有点都变成1状态,那么可能会若干方案都可以做到
那么其中存在需要最少操作次数的方案,打印这个最少操作次数
题目保证一定能做到所有点都变成1状态,并且没有重边和自环,所以我们可以假想出这样一个图来:
        1 --------------------------------------------- 4
         \  \           / /
          \  \         / /
           \   \     /  /
            2   \   /  /
             \   \ /  /
              \   5  /
               \  | /
                \ |/
                  3  
对于以上这个图,我们1~5的原始状态均为0,可以操作任意一个点,操作后该点以及相邻点的状态都会改变,根据这句话,我们可以得到:
对于1这个点而言,他自己变会导致自己也改变2,5,4改变也会让他改变,然而1和他相邻的点同时改变就相当于没改变
所以我们可以用异或来形容这些状态的改变(由于所有点的初始状态均为 0,而最终要求所有点都变成 1,因此每个点必须恰好被改变奇数次。
对于任意一个点 i,能够影响它的操作包括:
* 操作点 i 自己;
* 操作所有与点 i 相邻的点。
由于状态改变两次会回到原状态,因此可以使用异或表示状态变化。):
x1 ⊕ x2 ⊕ x4 ⊕ x5 = 1;(点3的改变不会影响1的状态)
根据这样的规律,我们可以列出异或版的高斯消元方程:
* 点1 与 2、4、5 相连(自己也算一个,因为操作自己会改变自己)
* 点2 与 1、3相连
* 点3 与 2、4、5 相连
* 点4 与 1、3、5 相连
* 点5 与 1、3、4 相连
所以:
设 x_i 表示是否操作点 i(1 表示操作,0 表示不操作),每个点的最终状态要变成 1(初始为 0,所以需要改变奇数次)。
对于点 1,能影响它的点是:{1, 2, 4, 5},所以方程是:
x1 ⊕ x2 ⊕ x4 ⊕ x5 = 1
对于点 2,影响它的点是:{1, 2, 3},所以:
x1 ⊕ x2 ⊕ x3= 1
点 3,影响它的点是:{2, 3, 4, 5}:
x2 ⊕ x3 ⊕ x4 ⊕ x5 = 1
点 4,影响它的点是:{1, 3, 4, 5}:
x1 ⊕ x3 ⊕ x4 ⊕ x5 = 1
点 5,影响它的点是:{1, 3, 4, 5}:
x1 ⊕ x3 ⊕ x4 ⊕ x5 = 1
写成矩阵的形式就是:
[1 1 0 1 1 | 1]
[1 1 1 0 0 | 1]
[0 1 1 1 1 | 1]
[1 0 1 1 1 | 1]
[1 0 1 1 1 | 1]
根据这样的形式继续消元,就可以得到这种图形下的操作次数
而在高斯消元后:
* 如果没有自由变量,则方程组只有唯一解,直接统计所有 x_i 中 1 的个数即可;
* 如果存在自由变量,则说明存在多种操作方案。枚举自由变量(dfs)的取值,并通过回代求出对应的完整方案,统计其中操作次数最少的方案。
最终得到的最小操作次数就是答案。

所以代码如下:

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
static int MAXN = 37;
static vector<vector<int>> graph;  // 增广矩阵,graph[i][j]表示第i个方程中x_j的系数
static vector<int> op;              // 存储每个变量的解(0或1)
static int n, ans;
// 异或高斯消元,将矩阵化为行阶梯形
static void guass(int n) {
    for (int i = 1; i <= n; i++) {          // i表示当前处理到第几个主元
        // 寻找第i列的主元(值为1的行)
        for (int j = 1; j <= n; j++) {
            // 如果j < i且graph[j][j]已经作为主元,跳过已经处理过的行
            if (j < i && graph[j][j] == 1) {
                continue;
            }
            // 找到第i列为1的行,交换到第i行
            if (graph[j][i] == 1) {
                swap(graph[i], graph[j]);
                break;
            }
        }
        // 如果graph[i][i]==1,说明找到了主元,用它消去其他行的第i列
        if (graph[i][i] == 1) {
            for (int j = 1; j <= n; j++) {
                if (i != j && graph[j][i] == 1) {
                    // 第j行异或第i行,消去第j行的第i列
                    for (int k = i; k <= n + 1; k++) {
                        graph[j][k] ^= graph[i][k];
                    }
                }
            }
        }
        // 如果graph[i][i]==0,说明第i列是自由变量,跳过
    }
}
// 初始化矩阵
static void prepare() {
    for (int i = 1; i <= n; i++) {
        // 清空第i行
        for (int j = 1; j <= n; j++) {
            graph[i][j] = 0;
        }
        // graph[i][i] = 1:操作自己会影响自己
        graph[i][i] = 1;
        // 方程右边为1:目标是把灯从0变成1
        graph[i][n + 1] = 1;
        op[i] = 0;
    }
}
// DFS枚举自由变量,从第n个变量往前搜索
// i: 当前处理的变量编号(从n到1)
// num: 当前已选择的操作次数
static void dfs(int i, int num) {
    // 剪枝:如果当前操作数已经大于等于已知最优解,没必要继续
    if (num >= ans) return;
    // 所有变量都处理完了,更新答案
    if (i == 0) {
        ans = num;
        return;
    }
    // 如果graph[i][i] == 0,说明第i个变量是自由变量(没有主元)
    if (graph[i][i] == 0) {
        // 自由变量可以取0或1,分别搜索
        // 情况1:取0
        op[i] = 0;
        dfs(i - 1, num);
        // 情况2:取1
        op[i] = 1;
        dfs(i - 1, num + 1);
    } else {
        // graph[i][i] == 1,说明第i个变量是非自由变量(有主元)
        // 根据方程 graph[i][i]*x_i + ... = graph[i][n+1]
        // 因为graph[i][i]=1,所以 x_i = graph[i][n+1] XOR (其他已知变量的和)
        int res = graph[i][n + 1];  // 方程右侧的值
        // 异或上所有已知变量(j > i的变量已经确定)
        for (int j = i + 1; j <= n; j++) {
            if (graph[i][j] == 1) {
                res ^= op[j];
            }
        }
        op[i] = res;
        dfs(i - 1, num + res);
    }
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    // 分配空间
    graph.assign(MAXN, vector<int>(MAXN));
    op.resize(MAXN);
    // 输入灯的数量
    cin >> n;
    prepare();
    // 输入边数
    int m;
    cin >> m;
    for (int i = 1, u, v; i <= m; i++) {
        cin >> u >> v;
        // 无向边:操作u会影响v,操作v会影响u
        graph[u][v] = 1;
        graph[v][u] = 1;
    }
    // 高斯消元
    guass(n);
    // 检查是否有自由变量
    // 如果所有graph[i][i]都是1,说明有唯一解,没有自由变量
    int sign = 1;
    for (int i = 1; i <= n; i++) {
        if (graph[i][i] == 0) {
            sign = 0;
            break;
        }
    }
    if (sign == 1) {
        // 没有自由变量,唯一解
        // 答案就是所有graph[i][n+1]为1的个数(因为每个变量取1就操作一次)
        ans = 0;
        for (int i = 1; i <= n; i++) {
            if (graph[i][n + 1] == 1) {
                ans++;
            }
        }
    } else {
        // 有自由变量,需要DFS枚举所有可能,找最少操作次数
        ans = n;  // 初始化答案为n(最大可能操作数)
        dfs(n, 0);
    }
    cout << ans << endl;
    return 0;
}

思考:

aiignore 复制代码
解题思路
这道题(洛谷 P2447 外星千足虫)的核心其实是"异或高斯消元"。
第一步:把"虫子"变成"方程"
题目说:足总数是奇数还是偶数(模 2)。
这本质上是告诉你:如果足数只有 0(偶数)和 1(奇数)两种状态,那"加法"就是"异或(XOR)"。
假设 x1, x2, ..., xN 代表第 i 只虫子,如果是奇数就是 1,偶数就是 0。
比如 Charles 选了虫子 1、2、3 放进机器,结果告诉你是 1(奇数)。
那么这个事实就可以写成方程:x1 异或 x2 异或 x3 = 1 (异或和为 1)。
我们一共有 M 条这样的方程,就有 M 行,每行包含 N 个系数(0 或 1)以及一个常数(0 或 1)。
第二步:利用异或的特性进行"高斯消元"
平时解线性方程组用的是加减乘除,这里只有 0 和 1,所以变成了按位异或:
1. 消元手段:把两行整体异或在一起(row1 异或 row2)。这就像把两个方程加起来。
2. 目标:像解二元一次方程组一样,把矩阵变成对角矩阵(即第一行只有 x1,第二行只有 x2...)。
3. 最终形态:如果消完后,对角线上全为 1,说明有唯一解。如果消着消着,发现某一列的主元找不到了(全为 0),那说明这个变量是"自由变量",也就是多解(Cannot Determine)。
第三步:巧妙地回答"第 K 次统计就能确定"(这是本题最妙的地方)
题目问:在第几次操作后,就足以确定唯一解了?
平时解方程,我们是等所有数据都给完了才消元。
但这道题,数据是一行一行顺序输入的。
所以做法是:边输入,边往上叠。我们在消元的过程中,如果为了得到主元,交换到了第 j 行,那说明我们必须用到第 j 次实验的结果。
所以,我们在寻找主元交换行时,记录下用到的最大的行号 j(need = max(need, j))。这个最大的行号,就是我们需要的最少实验次数!
第四步:巧用"位图(Bitset)加速"
因为 N 最大有 1000,M 最大有 2000。
如果是普通的二维数组,消元时每次异或都要循环 1000 次,复杂度 2000 * 1000 * 1000 = 2 * 10^9,必超时。
所以用了 bitset:
把每一行的系数(0/1 串)当成一整个长整数。
在 C++ 里,异或整个 bitset(row1 ^= row2)只需要 CPU 一条指令(甚至多条指令并行),速度直接快了 32 倍甚至 64 倍,完美卡进时间限制。
一句话总结解题思路:
把虫子的奇偶性当成未知数,把每次实验当成异或方程。用位图优化的异或高斯消元解方程。消元时记录要用的最大行号,消完后看对角线,对角线上全是 1 就是有唯一解(输出行号和答案),否则就是多解。

代码如下:

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 2002;
bitset<MAXN> mat[MAXN]; 
int n, m, s;
int need;
int get(int row, int col) {
    return mat[row][col] == 1 ? 1 : 0;
}
void eor(int row1, int row2) {
    mat[row2] ^= mat[row1];
}
void gauss(int n) {
    need = 0;
    for (int i = 1; i <= n; i++) {
        for (int j = i; j <= n; j++) {
            if (get(j, i) == 1) {
                // 直接用 C++ 标准库自带的 swap 函数交换两行
                swap(mat[i], mat[j]); 
                need = max(need, j);
                break;
            }
        }
        if (get(i, i) == 0) {
            return;
        }
        for (int j = 1; j <= n; j++) {
            if (i != j && get(j, i) == 1) {
                eor(i, j);
            }
        }
    }
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    cin >> n >> m;
    s = max(n, m);
    for (int i = 1; i <= m; i++) {
        string line;
        cin >> line;
        for (int j = 1; j <= n; j++) {
            if (line[j - 1] == '1') {
                mat[i].set(j);
            }
        }
        int val;
        cin >> val;
        if (val == 1) {
            mat[i].set(s + 1);
        }
    }
    gauss(s);
    int sign = 1;
    for (int i = 1; i <= n; i++) {
        if (get(i, i) == 0) {
            sign = 0;
            break;
        }
    }
    if (sign == 0) {
        cout << "Cannot Determine" << endl;
    } else {
        cout << need << endl;
        for (int i = 1; i <= n; i++) {
            if (get(i, s + 1) == 1) {
                cout << "?y7M#" << endl;
            } else {
                cout << "Earth" << endl;
            }
        }
    }
    return 0;
}
相关推荐
嘿嘿-661 小时前
gpt-6-astra测试,测试你的模型有没有降智
人工智能·gpt·chatgpt·web
sylviiiiiia1 小时前
leetcode hot 100
python·算法·leetcode
思录Echo1 小时前
光学轮廓仪质检设备厂家怎么选?优可测国产替代方案深度解析
大数据·人工智能
麻瓜code1 小时前
【Agent】AI 应用开发环境搭建 & 大模型三种接入方式对比
人工智能
R²AIN SUITE1 小时前
2026企业级AI Agent开发平台怎么选?RAG知识库到工作流编排的四层架构拆解
人工智能·架构
延凡科技2 小时前
建筑节能新视角:智慧冷站的数字化实践
java·人工智能·能源·暖通
zzzll11112 小时前
RAG(检索增强生成)会不会消亡?
人工智能·深度学习·机器学习
鹿鸣天涯2 小时前
Hugging Face AI开源平台学习入门
人工智能
Htr_2 小时前
Reflexio 使用指南:让 AI 智能体从每次交互中持续学习
人工智能·学习·交互