2020ICPC济南A Matrix Equation

题目链接

题目:

定义两种矩阵运算:
(1) Z = X × Y Z=X \times Y Z=X×Y,即 Z i , j = ( ∑ k = 1 N X i , k Y k , j ) m o d    2 Z_{i,j}=(\sum_{k=1}^N X_{i,k}Y_{k,j}) \mod 2 Zi,j=(k=1NXi,kYk,j)mod2
(2) Z = X ⊙ Y Z=X \odot Y Z=XY,即 Z i , j = X i , j Y i , j Z_{i,j}=X_{i,j}Y{i,j} Zi,j=Xi,jYi,j
给定两个 n × n n \times n n×n的01矩阵 A , B A,B A,B,问有多少个 n × n n \times n n×n的01矩阵 C C C满足 A × C = A ⊙ C A \times C=A \odot C A×C=AC
( 1 ≤ n ≤ 200 , A i , j , B i , j ∈ { 0 , 1 } ) (1 \le n \le 200,A_{i,j},B_{i,j} \in \{0,1\}) (1n200,Ai,j,Bi,j{0,1})

题解:

首先可以发现 C C C的列与列之间是没有关系的,所以我们可以单独地求解每一列的数量,然后用乘法原理合并答案。将 C C C的第 k k k列看成 n n n维向量,我们就可以得到以下方程组
( A i , 1 C 1 , k + A i , 2 C 2 , k + . . . + A i , n C n , k ) m o d    2 = B i , k C i , k , 1 ≤ i ≤ n (A_{i,1}C_{1,k}+A_{i,2}C_{2,k}+...+A_{i,n}C_{n,k})\mod 2=B_{i,k}C_{i,k},1 \le i \le n (Ai,1C1,k+Ai,2C2,k+...+Ai,nCn,k)mod2=Bi,kCi,k,1in
因为矩阵 A A A是01矩阵,又是模2意义下的加法,即异或运算,所以可以把上述方程组变成异或方程组。如
A 1 , ∙ = ( 1 , 0 , 1 , 1 ) , C ∙ , k = ( x 1 , x 2 , x 3 , x 4 ) T , B 1 , k = 1 A_{1,\bullet}=(1,0,1,1),C_{\bullet,k}=(x1,x2,x3,x4)^T,B_{1,k}=1 A1,=(1,0,1,1),C,k=(x1,x2,x3,x4)T,B1,k=1
那么可以得到
( x 1 + x 3 + x 4 ) m o d    2 = x 1 x 1 ⊕ x 3 ⊕ x 4 = x 1 x 3 ⊕ x 4 = 0 (x1+x3+x4)\mod2=x1\\ x1 \oplus x3 \oplus x4=x1\\ x3 \oplus x4=0 (x1+x3+x4)mod2=x1x1x3x4=x1x3x4=0
所以最后可以得到一个齐次的异或方程组,那么问题就转化为了这个方程组有多少个解,那么可以求出异或空间里系数矩阵的秩 r k r_k rk,那么自由元的数量为 n − r k n-r_k nrk,因为取值只有0,1,所以解的个数为 2 n − r k 2^{n-r_k} 2nrk,那么最后的答案就是 ∏ k = 1 n 2 n − r k \prod_{k=1}^n 2^{n-r_k} k=1n2nrk。关于怎么求秩,可以用异或高斯消元,也可以通过将矩阵的行向量插入线性基得到极大线性无关组来求,两种方法的复杂度都是 O ( n 4 ) O(n^4) O(n4)的,过不了这道题,这里考虑用 b i t s e t bitset bitset来存储行向量进行优化,复杂度降为 O ( n 4 / ω ) O(n^4/\omega) O(n4/ω)。(用高斯消元求秩的时候有个坑点,行和列要用两个指针指,因为遇到一个自由元后,行指针不变,列指针要右移一个位置)

复杂度: O ( n 4 / ω ) O(n^4/\omega) O(n4/ω)
代码:

1、高斯消元版

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<bitset>
#include<sstream>
#include<ctime>
//#include<chrono>
//#include<random>
//#include<unordered_map>
using namespace std;

#define ll long long
#define ls o<<1
#define rs o<<1|1
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define sz(x) (int)(x).size()
#define all(x) (x).begin(),(x).end()
const double pi=acos(-1.0);
const double eps=1e-6;
const int mod=998244353;
const int INF=0x3f3f3f3f;
const int maxn=205;
ll read(){
	ll x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int n;
bitset<maxn>a[maxn];
int ans[maxn];
int A[maxn][maxn],B[maxn][maxn];
int guass(int n){
	int res=0;
	for(int i=1,p=1;i<=n;i++){
		int m=p;
		if(!a[m][i]){
			for(int j=p+1;j<=n;j++){
				if(a[j][i]){
					m=j;
					break;
				}
			}
		}
		if(!a[m][i]){
			continue;
		}
		if(p!=m)swap(a[p],a[m]);
		for(int j=p+1;j<=n;j++){
			if(a[j][i]){
				a[j]^=a[p];
			}
		}
		++res;
		p++;
	}
	return res;
}
int qpow(int a,int p){
	int res=1;
	while(p){
		if(p&1)res=1ll*res*a%mod;
		a=1ll*a*a%mod;
		p>>=1;
	}
	return res;
}
int main(void){
	// freopen("in.txt","r",stdin);
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			scanf("%d",&A[i][j]);
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			scanf("%d",&B[i][j]);
		}
	}
	int ans=0;
	for(int k=1;k<=n;k++){
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				a[i][j]=A[i][j];
			}
			if(B[i][k])a[i][i]=a[i][i]^1;
		}
		ans+=n-guass(n);
	}
	printf("%d\n",qpow(2,ans));
	return 0;
}

2、线性基版(比赛中写的,码风不大对劲)

#include<bits/stdc++.h>
#include<bitset>
using namespace std;
const int maxn=205;
typedef long long ll;
const ll mod=998244353;
struct L{
	bitset<210>a[210];
	int maxl;
	void init(int n){
		maxl=n;
		for(int i=0;i<=n;i++){
			a[i].reset();
		}
	}
	int ins(bitset<210>t){
		for(int i=maxl;i>=0;i--){
			if(t[i]==0)continue;
			if(a[i].any())t^=a[i];
			else{
				for(int j=0;j<i;j++){
					if(t[j]==1)t^=a[j];
				}
				for(int j=i+1;j<=maxl;j++){
					if(a[j][i])a[j]^=t;
				}
				a[i]=t;
				break;
			}
		}
		if(t.any())return 1;
		else return 0;
	}
}L;
ll qpow(ll a,ll p){
	ll res=1;
	while(p){
		if(p&1)res=res*a%mod;
		a=a*a%mod;
		p>>=1;
	}
	return res;
}
int n;
int a[maxn][maxn],b[maxn][maxn];
void solve()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			scanf("%d",&a[i][j]);
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			scanf("%d",&b[i][j]);
		}
	}
	bitset<210>bt;
	ll ans=1;
	for(int k=1;k<=n;k++){
		L.init(n);
		int cnt=n;
		for(int i=1;i<=n;i++){
			bt.reset();
			for(int j=1;j<=n;j++){
				if(a[i][j])bt.set(j-1);
			}
			if(b[i][k])bt.flip(i-1);
			cnt-=L.ins(bt);
		}
		ans*=qpow(2,cnt);
		ans%=mod;
	}
	printf("%lld\n",ans%mod);
}

int main()
{
	int T=1;

	// scanf("%d",&T);
	for(int t=1;t<=T;t++)
	{
		solve();
	}

	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值