第 45 届国际大学生程序设计竞赛(ICPC)亚洲区域赛(济南)A. Matrix Equation(高斯消元)
链接:https://ac.nowcoder.com/acm/contest/10662/A
来源:牛客网
We call a matrix "01 Square" if and only if it's a N×NN×N matrix and its elements are all 00 or 11.
For two 01 Squares XX,YY, we define two operators X×YX×Y and X⊙YX⊙Y. The value of them are also 01 Square matrices and calculated below(we use ZZ to abbreviate X×YX×Y and DD to abbreviate X⊙YX⊙Y):
Zi,j=(∑k=1NXi,kYk,j) mod 2Zi,j=(∑k=1NXi,kYk,j) mod 2
Di,j=Xi,jYi,jDi,j=Xi,jYi,j
Now MianKing has two 01 Squares A,BA,B, he wants to solve the matrix equation below:
A×C=B⊙CA×C=B⊙C
You need to help MainKing solve this problem by calculating how many 01 Squares CC satisfy this equation.
The answer may be very large, so you only need to output the answer module 998244353998244353.
输入描述:
The first line has one integer NN
Then there are NN lines and each line has NN integers, the j-th integer of the i-th line denotes Ai,jAi,j
Then there are NN lines and each line has NN integers, the j-th integer of the i-th line denotes Bi,jBi,j
1≤N≤2001≤N≤200, Ai,j,Bi,j∈{0,1}Ai,j,Bi,j∈{0,1}
输出描述:
Output the answer module 998244353998244353.
示例1
输入
复制
2
0 1
1 1
1 0
0 1
输出
复制
2
示例2
输入
复制
3
1 0 0
0 1 0
0 0 1
1 1 1
1 1 1
1 1 1
输出
复制
512
示例3
输入
复制
4
0 1 0 1
0 1 1 0
0 1 1 1
1 0 0 1
1 0 1 1
0 1 1 1
1 0 0 1
1 1 1 0
输出
复制
8
比赛的时候没学线代相关的算法,现在看的话其实还是挺套路的2333
首先不难写出每个\(c_{ij}\)要满足的等式:
\(\Sigma_{i = 1}^{n}A_{ik}C_{kj} = B_{ij}C_{ij}\),同时注意到这个加法是在模2意义下的加法,因此其本质就是异或运算,那么这个等式可以写为:
\(\bigoplus_{i = 1}^{n}A_{ik}C_{kj} = B_{ij}C_{ij}\)。右边那一项含有要求的变元,同时\(C_{ij}\)正好也在左式,因此要把它移到左边。在左式中与\(B_{ij}C_{ij}\)对应的实际上是\(A_{ii}C_{ij}\),所以如果\(A_{ii} == B_{ij}\),那么移项后\(C_{ij}\)的系数实际上就是0了(两边同时异或上\(A_{ii}C_{ij}\));同理如果\(A_{ii} != B_{ij}\),那么移项后\(C_{ij}\)的系数实际上就是1。
矩阵C是列独立的,这样,我们取C矩阵的某一列(设为\(C_{i})\)作为列向量与A相乘:\(A\times C_{i} = B_{i}⊙C_{i}\),再移项实际上就是n个异或方程组,解出来的自由元的个数就是C的这一列能任意取值(0或1)的位置的个数,这样对于C的每一列都求出来自由元的个数,最终能够得到自由元的总数x,输出的答案就是\(2^x\)。
求解异或方程组可以用bitset优化的高斯消元来做。
#include <bits/stdc++.h>
#define mod 998244353
#define int long long
using namespace std;
int n, a[205][205], b[205][205], m[205][205];
long long fpow(long long a, long long b) {
long long ans = 1;
for(; b; b >>= 1) {
if(b & 1) ans = ans * a % mod;
a = a * a % mod;
}
return ans % mod;
}
bitset<220> M[205];
int Free;
int guass(int n, int m)//n row n 行 m col m 列
{
Free = 0;
int row = 0, col = 0, maxx;
for(; col < m; ++ col) {
for(maxx = row; maxx < n; ++ maxx)
if(M[maxx][col])
break;
if(maxx == n) continue;
if(M[maxx][col] == 0) continue;
swap(M[maxx], M[row]);
for(int i = row + 1; i < n; ++ i)
if(M[i][col]) //如果这一列有数的话那一行全部消掉
M[i] = M[i] ^ M[row];
row ++ ;
}
Free = n - row;
if(row < n) {
for(int i = row; i < n; ++ i)
if(M[i][m])//矛盾,出现非零的常数项等于0,说明无解
return 2;
return 1;//有无穷多组解
}
//行最简形矩阵,第i行第i列的表示的未知量x_i的一个解
for(int i = n - 1; i >= 0; -- i) {//第i行,第i行第i列
for(int j = i + 1; j < m; ++ j)//第j列,右边所有列都要消
M[i][m] = M[i][m] ^ (M[j][m] * M[i][j]);
}
return 0;
}
signed main() {
ios::sync_with_stdio(false);
cin >> n;
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
cin >> a[i][j];
}
}
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
cin >> b[i][j];
}
}
long long ans = 1, num = 0;
for(int j = 1; j <= n; j++) {//求c1j c2j ... cnj中的自由元个数
for(int i = 1; i <= n; i++) {
for(int k = 1; k <= n; k++) {
m[i][k] = a[i][k];
}
if(b[i][j] == a[i][i]) {
m[i][i] = 0;//相当于移项
} else {
//b[i][j]为0的话不影响
m[i][i] = 1;
}
}
//此时对m矩阵进行高斯消元解异或方程组,求自由元个数
//cout << endl << endl;
for(int ii = 1; ii <= n; ii++) {
for(int jj = 1; jj <= n; jj++) {
M[ii - 1][jj - 1] = m[ii][jj];
}
}
int result = guass(n, n);
if(result == 2) {
cout << 0;
return 0;
}
num += Free;
Free = 0;
}
ans = fpow(2, num);
cout << ans;
return 0;
}