G - Cross Xor
对于\((n\&1)\&\&(m\&1)\)的情况,所有行、列的异或和的必须相等(异或和指当前行/列中所有元素的异或和)
每次修改的点\((x_1,y_1)\),\((x_2,y_1)\),\((x_1,y_2)\),\((x_2,y_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;
}
标签:CF1672G,Xor,int,Cross,异或,权值,MOD
From: https://www.cnblogs.com/LuoyuSitfitw/p/17755128.html