说在前面
这道题真的好巧妙,看题解理解了好久…(ZKX:蠢死了)
交上去WA了,以为可能是哪里细节搞错了,然而最后发现是行和列搞反了= =???
题目
题目大意
现在有一个N*M矩阵,矩阵上只能填数字0或1
现在矩阵里已经有一些格子被填写了数字,询问是否存在一种填写方案使得「任意一个2*2的矩阵异或和为1」,输出方案总数
输入输出格式
输入格式:
第一行N , M , K,表示一个N行M列的矩阵,其中已经有K个格子填了数字
接下来K行,每行3个数字x,y,c表示第x行第y列的格子被填写了数字c
N,M,K均不超过1e6
输出格式:
输出方案总数对1000000000(9个0)取模的结果
解法
第
i
i
行列的格子用
a[i][j]
a
[
i
]
[
j
]
表示
考虑一个
2∗2
2
∗
2
的矩阵,假设之右下角为
(i,j)
(
i
,
j
)
,那么应该符合a[i][j] ^ a[i-1][j] ^ a[i][j-1] ^ a[i-1][j-1]=1,令这个式子为
S[i][j]
S
[
i
]
[
j
]
把从左上角到
(i,j)
(
i
,
j
)
所有的
S
S
,即S[2…i][2…j]全部异或起来,最后会得到a[1][1] ^ a[1][j] ^ a[i][1] ^ a[i][j]=[ i,j都是偶数 ]
可以发现,只要确定了第一行和第一列数字的值,整个矩阵就已经确定下来了。如果不考虑已经填的格子,答案就是
题目已经告诉了部分的a[i][j],而a[1][1]只有两种取值,确定了这两个,就可以知道a[1][j] ^ a[i][1]的值是多少,从而知道合法的方案数是多少。使用带权并查集维护这个异或关系(这个权,就是异或的值,如果冲突就可以直接判无解),最后答案就是
2并查集数−1
2
并
查
集
数
−
1
。
如果给的a[i][j]中有i=1或者j=1的情况,就相当于是已经确定了取值(而不是只知道异或关系),所以这样的i,j会和1在同一个并查集里,这个并查集里的数字都是确定的。这就是上面「并查集数-1」的原因
下面是自带大常数的代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;
int N , M , K , cnt , ans ;
bool flag[2] ;
struct Paint{
int x , y , c ;
Paint(){} ;
Paint( const int &x_ , const int &y_ , const int &z_ ):
x(x_) , y(y_) , c(z_){} ;
}P[1000005] ;
struct Union_set{
bool xorv[1000005*2] ;
int fa[1000005*2] ;
void init(){
for( int i = 1 ; i < N + M ; i ++ )
fa[i] = i , xorv[i] = 0 ;
}
int find( int x ){
if( fa[x] == x ) return x ;
int tmp = find( fa[x] ) ;
xorv[x] ^= xorv[ fa[x] ] ;
return fa[x] = tmp ;
}
bool Union( int a , int b , int xorv_ ){
int F_a = find( a ) , F_b = find( b ) ;
if( F_a == F_b ) return false ;
fa[F_b] = F_a;
xorv[F_b] = xorv_ ;
return true ;
}
}U ;
int mmod = 1e9 ;
int s_pow( int x , int b ){
long long rt = 1 ;
while( b ){
if( b&1 ) rt = rt * x %mmod ;
x = 1LL * x * x %mmod ; b >>= 1 ;
}
return rt ;
}
int solve(){
//a[1][j]^a[i][1] = a[1][1]^( i,j偶? 1 : 0 )^a[i][j] ;
U.init() ;
for( int i = 1 ; i <= cnt ; i ++ ){
int x = P[i].x , y = ( P[i].y == 1 ? 1 : P[i].y + N - 1 ) , c = P[i].c ;
int F_x = U.find( x ) , F_y = U.find( y ) ;
int tmp = U.xorv[x] ^ U.xorv[y] ^ c ;
if( F_x == F_y && tmp ) return 0 ;
U.Union( F_x , F_y , tmp ) ;
}
int mi = 0 ;
for( int i = 1 ; i < N + M ; i ++ )
if( U.fa[i] == i ) mi ++ ;
return s_pow( 2 , mi-1 ) ;
}
int main(){
flag[0] = flag[1] = true ;
scanf( "%d%d%d" , &N , &M , &K ) ;
for( int i = 1 , x , y , c ; i <= K ; i ++ ){
scanf( "%d%d%d" , &x , &y , &c ) ;
if( x == 1 && y == 1 ) flag[c] = false ;
else P[++cnt] = Paint( x , y , c^(!(x&1)&&!(y&1)) ) ;
}
//a[1][1]^a[1][j]^a[i][1] = ( i,j偶? 1 : 0 )^a[i][j] ;
if( flag[1] ) ans += solve( ) ;
if( flag[0] ){
for( int i = 1 ; i <= cnt ; i ++ )
if( P[i].x > 1 && P[i].y > 1 ) P[i].c ^= 1 ;
ans += solve( ) ;
}
printf( "%d" , ans%mmod ) ;
}