[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 ) ; }
相关文章推荐
- BZOJ2303 APIO2011方格染色(并查集)
- 【BZOJ2303】【Apio2011】方格染色 异或方程+并查集
- [BZOJ2303][APIO2011]方格染色 异或+并查集
- [杂题 异或 带权并查集] BZOJ2303: [Apio2011]方格染色
- [BZOJ2303][Apio2011]方格染色(数学相关+加权并查集)
- BZOJ 2303: [Apio2011]方格染色 [并查集 数学!]
- BZOJ2303: [Apio2011]方格染色
- BZOJ2303: [Apio2011]方格染色
- BZOJ 2303: [Apio2011]方格染色 并查集神题
- BZOJ2303 APIO2011方格染色
- BZOJ2303: [Apio2011]方格染色
- [BZOJ 2303][Apio2011]方格染色:并查集
- BZOJ_2303_[Apio2011]方格染色 _并查集
- bzoj 2303: [Apio2011]方格染色【并查集】
- bzoj 2303: [Apio2011]方格染色 (并查集)
- [BZOJ]2303: [Apio2011]方格染色 并查集
- [APIO2011]方格染色
- bzoj2303 方格染色 并查集
- [Apio2011]方格染色
- BZOJ 2303: [Apio2011]方格染色 题解