[CF1672G]Cross Xor

发布时间 2023-10-10 16:52:17作者: LuoyuSitfitw

G - Cross Xor

对于\((n\&1)\&\&(m\&1)\)的情况,所有行、列的异或和的必须相等(异或和指当前行/列中所有元素的异或和)

  1. 每次修改的点\((x_1,y_1)\)\((x_2,y_1)\)\((x_1,y_2)\)\((x_2,y_2)\)使得所有行和列的异或和不会改变

  2. 只对\((i,j)\)进行一次操作,那么所有行和列的异或和都会改变

对于\(n\)\(m\)一奇一偶同理,此时只需要行(\(m\&1\))或者列(\(n\&1\))的异或和相等

然后对于\((n\&1)\&\&(m\&1)\)的解法:

将每一行每一列看作一个点,有\(?\)就连接着两个点,那么对于生成的图,考虑随便找出一棵生成树,那么我们的目的就是让所有点的权值(即其对应的异或和)相等,我们钦定所有点的权值为\(x(x=0/1)\),若当前点的权值不为\(x\),那么将其连向父亲的边的权值改成\(1\),这样这个点与其父亲的权值都要\(xor\ 1\),那么无论生成树外的边如何选择,生成树上的边总有唯一一种选择使得合法,所以答案是 \(2^{边数−点数+1}\),对每个连通块分别做一次即可

#include<bits/stdc++.h>
#define pb push_back
using namespace std;
const int N=2e3+5,MOD=998244353;
int n,m,a[N][N],cnt_all;
int val[N<<1],vval[N<<1],c[N][N],cnt2[N];
int dp[2],prod[2];
vector<int> edge[N<<1];
int power(int x,int y){
	int ans=1;
	for(;y;y>>=1,x=1ll*x*x%MOD) if(y&1) ans=1ll*ans*x%MOD;
	return ans;
}
int add(int x,int y){ x+=y; if(x>=MOD) x-=MOD; return x; }
void ad(int &x,int y){ x+=y; if(x>=MOD) x-=MOD; }
int now,num_edge,num_point;
bool flag,vis[N<<1];
void dfs(int u,int fa){
	vis[u]=true,++num_point;
	for(auto v:edge[u]){
		++num_edge;
		if(!vis[v]) dfs(v,u);
		if(flag) return;
	}
	if(val[u]^now){
		if(!fa) return flag=true,void();
		val[u]^=1,val[fa]^=1;
	}
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i){
		getchar();
		for(int j=1;j<=m;++j) a[i][j]=min(getchar()-'0',2),cnt_all+=(a[i][j]==2);
	}
	if(!(n&1)&&!(m&1)) return printf("%d",power(2,cnt_all)),0;
	if((n&1)&&!(m&1)){
		for(int i=1;i<=n;++i)
			for(int j=1;j<=m;++j){
				if(a[i][j]<2) val[j]^=a[i][j];
				else ++cnt2[j];
			}
		for(int i=0;i<=max(n,m);++i){
			c[i][0]=1;
			for(int j=1;j<=i;++j) c[i][j]=add(c[i-1][j],c[i-1][j-1]);
		}
		prod[0]=prod[1]=1;
		for(int i=1;i<=m;++i){
			dp[0]=dp[1]=0;
			for(int j=0;j<=cnt2[i];++j) ad(dp[val[i]^(j&1)],c[cnt2[i]][j]);
			prod[0]=1ll*prod[0]*dp[0]%MOD,prod[1]=1ll*prod[1]*dp[1]%MOD;
		}
		return printf("%d",add(prod[0],prod[1])),0;			
	}
	if(!(n&1)&&(m&1)){
		for(int i=0;i<=n;++i)
			for(int j=1;j<=m;++j){
				if(a[i][j]<2) val[i]^=a[i][j];
				else ++cnt2[i];
			}
		for(int i=0;i<=max(n,m);++i){
			c[i][0]=1;
			for(int j=1;j<=i;++j) c[i][j]=add(c[i-1][j],c[i-1][j-1]);
		}
		prod[0]=prod[1]=1;
		for(int i=1;i<=n;++i){
			dp[0]=dp[1]=0;
			for(int j=0;j<=cnt2[i];++j) ad(dp[val[i]^(j&1)],c[cnt2[i]][j]);
			prod[0]=1ll*prod[0]*dp[0]%MOD,prod[1]=1ll*prod[1]*dp[1]%MOD;
		}
		return printf("%d",add(prod[0],prod[1])),0;	
	}
	for(int i=1;i<=n;++i)
		for(int j=1;j<=m;++j){
			if(a[i][j]<2) val[i]^=a[i][j],val[j+n]^=a[i][j],vval[i]=val[i],vval[j+n]=val[j+n];
			else edge[i].pb(j+n),edge[j+n].pb(i);
		}
	int prod=1,ans=0;
	for(int i=1;i<=n+m;++i)
		if(!vis[i]){
			flag=num_edge=num_point=0,dfs(i,0);
			if(flag){ prod=0; break; }
			num_edge/=2,prod=1ll*prod*power(2,num_edge-num_point+1)%MOD;
		}
	ad(ans,prod); 
	prod=now=1; for(int i=1;i<=n+m;++i) val[i]=vval[i],vis[i]=false;
	for(int i=1;i<=n+m;++i)
		if(!vis[i]){
			flag=num_edge=num_point=0,dfs(i,0);
			if(flag){ prod=0; break; }
			num_edge/=2,prod=1ll*prod*power(2,num_edge-num_point+1)%MOD;
		}
	printf("%d",add(ans,prod));
	return 0;
}