【学习笔记】CF1672G Cross Xor

46 篇文章 2 订阅
3 篇文章 0 订阅

R i R_i Ri表示第 i i i行元素异或和, L i L_i Li表示第 i i i列元素异或和。那么矩阵全为零时显然 L i , R i L_i,R_i Li,Ri全为零。

如果 n n n, m m m都是偶数,那么每次操作 ( i , j ) (i,j) (i,j)相当于把 L k ( k ≠ i ) L_k(k\ne i) Lk(k=i) R k ( k ≠ j ) R_k(k\ne j) Rk(k=j) 翻转。我们可以看成只对 L i , R j L_i,R_j Li,Rj翻转,这又相当于只翻转 ( i , j ) (i,j) (i,j)这一个点,因此任意 01 01 01矩阵都是可以还原的。

如果 n n n是奇数, m m m是偶数,那么操作 ( i , j ) (i,j) (i,j)相当于把 L k ( k ≠ i ) L_k(k\ne i) Lk(k=i)和所有 R k R_k Rk翻转,发现只和 i i i有关,结论是 R k R_k Rk必须全为 0 0 0或全为 1 1 1

求方案数则对于每个右部点钦定一条边即可。

如果 n , m n,m n,m都是奇数,那么操作 ( i , j ) (i,j) (i,j)相当于把所有 L k L_k Lk R k R_k Rk翻转,于是结论很明显,必须初始全为 0 0 0或全为 1 1 1

至于计数部分,如果 ( i , j ) (i,j) (i,j)是问号就在二分图上连一条边,对于一个连通块要对每条边定 0 / 1 0/1 0/1边权使点权满足奇偶性。可以考虑构建生成树,对于不在生成树上的边可以随便定,对于在生成树上的边的边权是唯一确定的。

#include<bits/stdc++.h>
#define fi first
#define se second
#define ll long long
#define pb push_back
#define db double
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
const int mod=998244353;
int n,m,K,W,num,vis[4005],in[4005],L[2005],R[2005];
int V,E;
char s[2005][2005];
vector<int>g[4005];
ll fpow(ll x,ll y){
	ll z(1);for(;y;y>>=1){
		if(y&1)z=z*x%mod;
		x=x*x%mod;
	}return z;
}
void dfs(int u){
	vis[u]=num,V++;
	W^=(u<=n)?L[u]:R[u-n];
	for(auto v:g[u]){
		if(!vis[v]){
			dfs(v);
		}if(u<=n&&vis[v]==num)E++;
	}
}
ll solve(int f){
	ll res=1;
	for(int i=1;i<=n+m;i++)vis[i]=0;
	for(int i=1;i<=n+m;i++){
		if(!vis[i]){
			num++,V=E=W=0,dfs(i);
			if(W!=f*V%2)return 0;
			res=res*fpow(2,E-V+1)%mod;
		}
	}return res;
}
ll solve1(int f){
	int c=0;
	for(int i=1;i<=m;i++){
		if(R[i]!=f&&!in[n+i]){
			return 0;
		}if(in[n+i])c++;
	}return fpow(2,K-c);
}
ll solve2(int f){
	int c=0;
	for(int i=1;i<=n;i++){
		if(L[i]!=f&&!in[i]){
			return 0;
		}if(in[i])c++;
	}
	return fpow(2,K-c);
}
int main(){
	cin>>n>>m;for(int i=1;i<=n;i++){
		scanf("%s",s[i]+1);
		for(int j=1;j<=m;j++){
			if(s[i][j]=='1')L[i]^=1,R[j]^=1;
			if(s[i][j]=='?')in[i]++,in[n+j]++,K++,g[i].pb(n+j),g[n+j].pb(i);
		}
	}
	if(n%2==0&&m%2==0){
		cout<<fpow(2,K);
	}
	else if(n%2==1&&m%2==0){
		cout<<(solve1(0)+solve1(1))%mod;
	}
	else if(n%2==0&&m%2==1){
		cout<<(solve2(0)+solve2(1))%mod;
	}
	else {
		cout<<(solve(0)+solve(1))%mod;
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值