[模板] 异或高斯消元

[模板] 异或高斯消元

开关问题

对异或矩阵进行消元,与普通实数消元可以说仅有一处不同:

当枚举到每一行进行异或消元时,当且仅当这一行的当前主元存在才可以消元。(否则矩阵的意义就改变了)

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 35;

int n;
int a[N][N];

int gauss()
{
    int r,c;
    for(r=1,c=1;c<=n;c++)
    {
        // 找主元
        int t = r;
        for(int i=r+1;i<=n;i++)
            if(a[i][c])
                t=i;
        if(!a[t][c])continue;//不保证唯一解--不一定有主元,相当于把那些自由元的行移动到最后面去
        // 交换主元行至r行
        for(int i=c;i<=n+1;i++)swap(a[t][i],a[r][i]);
        // 用第r行的a[r][j]消第i行的a[i][j] 前提是第i行的最左边是1(有主元)
        for(int i=r+1;i<=n;i++)
            for(int j=n+1;j>=c;j--)
            //等价于if(a[i][c]) a[i][j]^=a[r][j]
                a[i][j] ^= a[i][c] & a[r][j];
        //不能写a[i][j] ^= a[i][r] & a[r][j];
        //是因为可能存在中间无主元的情况 此时r不++了 c还在循环中++ 导致r!=c
        r++;
    }
    int res = 1;
    //此时已经到了全零行
    if (r < n + 1)
    {
        for (int i = r; i <= n; i ++ )
        {
            // 全零行的右边出现非零 无解
            if (a[i][n + 1]) return -1;  // 出现了 0 == !0,无解
            res *= 2;
        }
    }
    return res;
}

int main()
{
    int T;
    cin >> T;
    while(T--)
    {
        memset(a,0,sizeof a);
        cin >> n;
        for(int i=1;i<=n;i++) cin >> a[i][n+1];//初始状态
        for(int i=1;i<=n;i++)
        {
            int t;
            cin >> t;
            a[i][n+1]^=t;//关心a[i][n+1](初始状态)和t(新状态)有没有发生变化
            a[i][i]=1;//第i个开关一定会改变第i个灯
        }
        int x,y;
        while(scanf("%d%d",&x,&y),x||y)
            a[y][x]=1;
            //第y个等式(行)的右边(开关y状态) 左边(操作开关x)(第x列)的系数=1
        int t = gauss();
        if (t == -1) puts("Oh,it's impossible~!!");
        else printf("%d\n", t);
    }
    return 0;
}

同时,为了便于统计答案,可以在消元的过程中记录一个 \(r\) 变量来看当前消元到了多少行。

posted @ 2021-08-12 17:34  ¶凉笙  阅读(66)  评论(0编辑  收藏  举报