题目链接

题意
\(n*n\)\(01\)矩阵,对于每一个\(k(1<=k<=n)\),是否能选出若干行对每列进行异或,使得只有第\(k\)列为\(1\),其他列为\(0\)

思路:
线性基三大性质:
\(1\).原序列里面的任意一个数都可以由线性基里面的一些数异或得到。
\(2\).线性基里面的任意一些数异或起来都不能得到\(0\)
\(3\).线性基里面的数的个数唯一,并且在保持性质一的前提下,数的个数是最少的。

若某个数能由原序列中的数异或得到,则一定可以由线性基中的数唯一异或表示。
将每行看成一个数的二进制表示,得到\(n\)个数的线性基,并保存每个线性基由哪些行异或得到。
\(k\)列为\(1\),其他列为\(0\)表示的数为\(2^{k-1}\),得到其由哪些线性基异或得到,统计表示线性基的行序号。

code:

#include <bits/stdc++.h>
#define ll long long 
const double eps = 1e-15;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const double pi = acos(-1.0);
const int mod =  998244353;
const int maxn = 1e4 + 10;

using namespace std;

int n, vis[maxn];
int v[550][550], p[550][550];
int rec[550][550], ret[550], cnt[550];

void f(int a, int b){
    for(int i = 1; i <= n; i ++)v[a][i] = (v[a][i] + p[b][i]) % 2;
    for(int i = 1; i <= n; i ++)ret[i] = (ret[i] + rec[b][i]) % 2;
}

int main(){
    scanf("%d", &n);
    for(int i = 1; i <= n; i ++){
        for(int j = 1; j <= n; j ++)scanf("%d", &v[i][j]);
    }
    for(int i = 1; i <= n; i ++){
        memset(ret, 0, sizeof ret);
        for(int j = 1; j <= n; j ++){
            if(!v[i][j])continue;
            if(vis[j]) f(i, j);
            else {
                vis[j] = 1, ret[i] = (ret[i] + 1) % 2;
                for(int k = 1; k <= n; k ++)p[j][k] = v[i][k];
                for(int k = 1; k <= n; k ++)rec[j][k] = ret[k];
                break;
            }
        }
    }
    for(int i = 1; i <= n; i ++){
        if(!vis[i])return puts("-1"), 0;
    }

    for(int i = 1; i <= n; i ++){
        memset(cnt, 0, sizeof cnt), memset(ret, 0, sizeof ret);
        cnt[i] = 1;
        for(int j = i; j <= n; j ++){
            if(cnt[j]){
                for(int k = 1; k <= n; k ++){
                    cnt[k] = (cnt[k] + p[j][k]) % 2;
                    ret[k] = (ret[k] + rec[j][k]) % 2;
                }
            }
        }
        for(int i = 1; i <= n; i ++)if(ret[i])printf("%d ", i);
        puts("");
    }
    return 0;
}