您的位置:首页 > 其它

[BZOJ2303]-[Apio2011]方格染色-并查集+题目性质

2017-12-08 14:40 281 查看

说在前面

这道题真的好巧妙,看题解理解了好久…(ZKX:蠢死了)

交上去WA了,以为可能是哪里细节搞错了,然而最后发现是行和列搞反了= =???

题目

BZOJ2303传送门

题目大意

现在有一个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行j列的格子用a[i][j]表示

考虑一个2*2的矩阵,假设之右下角为[ i , j ],那么应该符合a[i][j] ^ a[i-1][j] ^ a[i][j-1] ^ a[i-1][j-1]=1,令这个式子为S[i][j]。

把从左上角到[ i , j ]所有的S,即S[2…i][2…j]全部异或起来,最后会得到a[1][1] ^ a[1][j] ^ a[i][1] ^ a[i][j] =[ i,j都是偶数 ]

可以发现,只要确定了第一行和第一列数字的值,整个矩阵就已经确定下来了。如果不考虑已经填的格子,答案就是2M+N−1

题目已经告诉了部分的a[i][j],而a[1][1]只有两种取值,确定了这两个,就可以知道a[1][j] ^ a[i][1]的值是多少,从而知道合法的方案数是多少。使用带权并查集维护这个异或关系,最后答案就是2并查集数−1。

对于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 ) ;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: