2020ICPC济南站 A题 Matrix Equation高斯消元求异或方程组

2020ICPC济南站 A题 Matrix Equation高斯消元求异或方程组


传送门: https://ac.nowcoder.com/acm/contest/10662/A

题意

A × C = B ⋅ C , A×C=B⋅C, A×C=BC求C的个数。

思路

比赛的时候猜答案是2的幂次,确实没错,然后想解法是先求A的逆,最后答案为 C = A − 1 × B ⋅ C C=A^{-1}×B⋅C C=A1×BC,那么就意味着, A − 1 × B A^{-1}×B A1×B这个结果,如果为0,那么可以去0或1,如果是1,那么只能取1,所以答案为 2 0 的 个 数 2^{0的个数} 20,可是,A可能没有逆,所以这个方法是行不通的。

先来观察式子:
[ A 11 A 12 ⋯ A 1 n A 21 A 22 ⋯ A 2 n ⋮ ⋮ ⋱ ⋮ A n 1 A n 2 ⋯ A n n ] × [ C 11 C 12 ⋯ C 1 n C 21 C 22 ⋯ C 2 n ⋮ ⋮ ⋱ ⋮ C n 1 C n 2 ⋯ C n n ] = [ B 11 ∗ C 11 B 12 ∗ C 12 ⋯ B 1 n ∗ C 1 n B 21 ∗ C 21 B 22 ∗ C 22 ⋯ B 2 n ∗ C 2 n ⋮ ⋮ ⋱ ⋮ B n 1 ∗ C n 1 B n 2 ∗ C n 2 ⋯ B n n ∗ C n n ] \begin{bmatrix} A_{11}& A_{12}& \cdots & A_{1n}\\ A_{21}& A_{22}& \cdots & A_{2n}\\ \vdots & \vdots & \ddots & \vdots \\ A_{n1}& A_{n2}& \cdots &A_{nn} \end{bmatrix}× \begin{bmatrix} C_{11}& C_{12}& \cdots & C_{1n}\\ C_{21}& C_{22}& \cdots & C_{2n}\\ \vdots & \vdots & \ddots & \vdots \\ C_{n1}& C_{n2}& \cdots &C_{nn} \end{bmatrix}=\begin{bmatrix} B_{11}*C_{11}& B_{12}*C_{12}& \cdots & B_{1n}*C_{1n}\\ B_{21}*C_{21}& B_{22}*C_{22}& \cdots & B_{2n}*C_{2n}\\ \vdots & \vdots & \ddots & \vdots \\ B_{n1}*C_{n1}& B_{n2}*C_{n2}& \cdots &B_{nn}*C_{nn} \end{bmatrix} A11A21An1A12A22An2A1nA2nAnn×C11C21Cn1C12C22Cn2C1nC2nCnn=B11C11B21C21Bn1Cn1B12C12B22C22Bn2Cn2B1nC1nB2nC2nBnnCnn

根 据 矩 阵 乘 法 可 以 知 道 C 矩 阵 第 一 列 的 方 程 组 为 : 根据矩阵乘法可以知道C矩阵第一列的方程组为: C
[ A 11 ∗ C 11 + A 12 ∗ C 21 + . . . + A 1 n ∗ C n 1 = B 11 ∗ C 11 ⋮ A 21 ∗ C 11 + A 22 ∗ C 21 + . . . + A 2 n ∗ C n 1 = B 21 ∗ C 21 ⋮ A n 1 ∗ C 11 + A n 2 ∗ C 21 + . . . + A n n ∗ C n 1 = B n 1 ∗ C n 1 ] \begin{bmatrix} A_{11}*C_{11}+A_{12}*C_{21}+...+A_{1n}*C_{n1}=B_{11}*C_{11}\\ \vdots\\ A_{21}*C_{11}+A_{22}*C_{21}+...+A_{2n}*C_{n1}=B_{21}*C_{21}\\ \vdots\\ A_{n1}*C_{11}+A_{n2}*C_{21}+...+A_{nn}*C_{n1}=B_{n1}*C_{n1} \end{bmatrix} A11C11+A12C21+...+A1nCn1=B11C11A21C11+A22C21+...+A2nCn1=B21C21An1C11+An2C21+...+AnnCn1=Bn1Cn1

[ ( A 11 − B 11 ) ∗ C 11 + A 12 ∗ C 21 + . . . + A 1 n ∗ C n 1 = 0 A 21 ∗ C 11 + ( A 22 − B 21 ) ∗ C 21 + . . . + A 2 n ∗ C n 1 = 0 ⋮ A n 1 ∗ C 11 + A n 2 ∗ C 21 + . . . + ( A n n − B n 1 ) ∗ C n 1 = 0 ] \begin{bmatrix} (A_{11}-B_{11})*C_{11}+A_{12}*C_{21}+...+A_{1n}*C_{n1}=0\\ A_{21}*C_{11}+(A_{22}-B_{21})*C_{21}+...+A_{2n}*C_{n1}=0\\ \vdots\\ A_{n1}*C_{11}+A_{n2}*C_{21}+...+(A_{nn}-B_{n1})*C_{n1}=0 \end{bmatrix} (A11B11)C11+A12C21+...+A1nCn1=0A21C11+(A22B21)C21+...+A2nCn1=0An1C11+An2C21+...+(AnnBn1)Cn1=0

所 以 每 列 都 是 相 互 独 立 的 , 所 以 我 们 求 每 列 的 种 数 , 最 后 全 部 乘 起 来 就 好 。 所以每列都是相互独立的,所以我们求每列的种数,最后全部乘起来就好。

由 于 这 个 矩 阵 乘 法 有 一 个 要 求 , 最 后 求 得 的 要 相 加 并 % 2 , 这 是 什 么 ? 这 不 就 是 异 或 吗 ? 由于这个矩阵乘法有一个要求,最后求得的要相加并\%2,这是什么?这不就是异或吗? %2

那 么 答 案 是 多 少 ? 每 一 列 的 种 数 是 多 少 ? 答 案 就 是 上 面 每 一 个 异 或 方 程 组 的 自 由 元 的 个 数 n − r , r 为 方 程 组 的 秩 。 每 个 值 都 可 以 取 0 或 1 , 即 答 案 为 2 n − r 。 最 后 将 所 有 列 的 种 数 相 乘 即 可 。 那么答案是多少?每一列的种数是多少?答案就是上面每一个异或方程组的自由元的个数n-r,r为方程组的秩。每个值都可以取0或1,即答案为2^{n-r}。最后将所有列的种数相乘即可。 nrr012nr

Code()

#include "bits/stdc++.h"

using namespace std;

#define FZT_ACM_LOCAL 1

typedef long long ll;
typedef long double ld;
typedef pair<int, int> pdd;

#define INF 0x3f3f3f3f
#define lowbit(x) x & (-x)
#define mem(a, b) memset(a , b , sizeof(a))
#define FOR(i, x, n) for(int i = x;i <= n; i++)

 const ll mod = 998244353;
// const ll mod = 1e9 + 7;
// const double eps = 1e-6;
// const double PI = acos(-1);
// const double R = 0.57721566490153286060651209;


const int N = 210;
int a[N][N];//增广矩阵
int x[N];//解集
int freeX[N];//自由变元
// equ:方程个数 var:变量个数
int Gauss(int equ, int var) {//返回自由变元个数
    /*初始化*/
    for (int i = 0; i <= var; i++) {
        x[i] = 0;
        freeX[i] = 0;
    }

    /*转换为阶梯阵*/
    int col = 0;//当前处理的列
    int num = 0;//自由变元的序号
    int k;//当前处理的行
    for (k = 0; k < equ && col < var; k++, col++) {//枚举当前处理的行
        int maxr = k;//当前列绝对值最大的行
        for (int i = k + 1; i < equ; i++) {//寻找当前列绝对值最大的行
            if (a[i][col] > a[maxr][col]) {
                maxr = i;
                swap(a[k], a[maxr]);//与第k行交换
                break;
            }
        }
        if (a[k][col] == 0) {//col列第k行以下全是0,处理当前行的下一列
            freeX[num++] = col;//记录自由变元
            k--;
            continue;
        }

        for (int i = k + 1; i < equ; i++) {
            if (a[i][col] != 0) {
                for (int j = col; j < var + 1; j++) {//对于下面出现该列中有1的行,需要把1消掉
                    a[i][j] ^= a[k][j];
                }
            }
        }
    }

    /*求解*/
    //无解:化简的增广阵中存在(0,0,...,a)这样的行,且a!=0
    for (int i = k; i < equ; i++)
        if (a[i][col] != 0)
            return -1;

    //无穷解: 在var*(var+1)的增广阵中出现(0,0,...,0)这样的行
    if (k < var)//返回自由变元数
        return var - k;//自由变元有var-k个

    //唯一解: 在var*(var+1)的增广阵中形成严格的上三角阵
    for (int i = var - 1; i >= 0; i--) {//计算解集
        x[i] = a[i][var];
        for (int j = i + 1; j < var; j++)
            x[i] ^= (a[i][j] && x[j]);
    }
    return 0;
}

ll quick_pow(ll a, ll b) {
    ll ans = 1;
    while(b) {
        if(b & 1) ans = ans * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return ans % mod;
}
int A[N][N];
int B[N][N];

void solve() {
    int n;
    cin >> n;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            cin >> A[i][j];
        }
    }
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            cin >> B[i][j];
        }
    }

    ll ans = 0;

    for (int k = 0; k < n; k++) {
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                a[i][j] = A[i][j];
            }
            if (B[i][k]) a[i][i] ^= 1;
        }
        int cnt = Gauss(n, n);
        ans += cnt > 0 ? cnt : 0;
    }

    cout << quick_pow(2, ans) << endl;
}

signed main() {
    solve();
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值