1. 程式人生 > >BZOJ 2303 方格染色 (並查集+數學相關)

BZOJ 2303 方格染色 (並查集+數學相關)

2303: [Apio2011]方格染色

Time Limit: 20 Sec  Memory Limit: 256 MB
Submit: 2309  Solved: 879
[Submit][Status][Discuss]

Description

Sam和他的妹妹Sara有一個包含n × m個方格的
表格。她們想要將其的每個方格都染成紅色或藍色。
出於個人喜好,他們想要表格中每個2 ×   2的方形區
域都包含奇數個(1 個或 3 個)紅色方格。例如,右
圖是一個合法的表格染色方案(在列印稿中,深色代
表藍色,淺色代表紅色) 。 
可是昨天晚上,有人已經給表格中的一些方格染上了顏色!現在Sam和Sara
非常生氣。不過,他們想要知道是否可能給剩下的方格染上顏色,使得整個表格
仍然滿足她們的要求。如果可能的話,滿足他們要求的染色方案數有多少呢? 

 

Input

輸入的第一行包含三個整數n, m和k,分別代表表格的行數、列數和已被染
色的方格數目。 
之後的k行描述已被染色的方格。其中第 i行包含三個整數xi, yi和ci,分別
代表第 i 個已被染色的方格的行編號、列編號和顏色。ci為 1 表示方格被染成紅
色,ci為 0表示方格被染成藍色。 

Output

輸出一個整數,表示可能的染色方案數目 W 模 10^9得到的值。(也就是說,如果 W大於等於10^9,則輸出 W被10^9除所得的餘數)。 

對於所有的測試資料,2 ≤ n, m ≤ 106
,0 ≤ k ≤ 10^6
,1 ≤ xi ≤ n,1 ≤ yi ≤ m。 
 

Sample Input

3 4 3
2 2 1
1 2 0
2 3 1

Sample Output

8

HINT

 

資料為國內資料+國際資料+修正版

 

鳴謝GYZ

 

思路:參考hzwer。幾個性質:

一、如果確定了第一行,接下來的每一行只會是

       變換1.上一行所有奇數列異或1後得到

       變換2.上一列所有偶數列異或1後得到

二、判斷無解的情況

       給定某一行的兩列a,b顏色ca,cb,我們就可以得出第一行這兩列同色(0)or反色(1)

      1.奇偶性相同的列(a mod 2 = b mod 2),第一行a,b列顏色關係為ca ^ cb

      2.奇偶性相同的列(a mod 2 <> b mod 2),設第一行到此行進行p1次變換1,p2次變換2

      則p1+p2=行號-1

      第一行a,b列顏色關係為cx^cy^(行號-1)(沒理解,待補)

                                      對於fa[],vis[]陣列的理解

第一種情況:藍色的點是第一行的編號,D與A是在同一列,那麼E的顏色就是唯一的

第二種情況:對於FG兩個點,他們的關係是制約的,所以他們在一個聯通塊裡,但是計算答案*2

#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
#include<string>
#include<map>
#include<queue>
#include<vector>
#include<stack>
#define ll long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define SWAP(x,y,t) ( (t)=(x),(x)=(y),(y)=(t) )
const int p=1e9;
using namespace std;
int n,m,K,tot;
int fa[1000005],F[1000005],c[1000005];
bool mark[1000005],vis[1000005];
vector<int> r[1000005],col[1000005];
ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int qpow(ll a,ll b)
{
	ll ans=1;
	for(int i=b;i;i>>=1,a=(a*a)%p)
		if(i&1) ans=(ans*a)%p;
	return ans;
} 
int find(int x)
{
	return x==fa[x]?x:fa[x]=find(fa[x]);
}
int find2(int x)
{
	if(x==F[x]) return x;
	int tmp=find2(F[x]);
	c[x]^=c[F[x]];
	return F[x]=tmp;
}
bool add(int a,int b,int f)
{
	int p=find2(a),q=find2(b);
	if(p==q) return (c[a]^c[b])==f;
	else
	{
		F[p]=q;
		c[p]=(c[a]^c[b]^f);
		return 1;
	}
}
int main()
{
	n=read();m=read();K=read();
	for(int i=1;i<=m;i++) fa[i]=F[i]=i;
	for(int i=1;i<=K;i++)
	{
		int a=read(),b=read(),c=read();
		if(a==1) vis[b]=1;
		mark[a]=1;
		r[a].push_back(b);
		col[a].push_back(c);
	}
	for(int i=1;i<=n;i++)
		for(int j=1;j<r[i].size();j++)
		{
			int x=r[i][j],y=r[i][j-1],cx=col[i][j],cy=col[i][j-1];
			int p=find(x),q=find(y);
			fa[p]=q;
			if(vis[p]) vis[q]=1;//vis[]和fa[]表示是否與第一行相關
			int t=cx^cy;
			if(x%2!=y%2) t=(t^(i-1))&1;
			if(!add(x,y,t)){puts("0");return 0;}
		}
	for(int i=1;i<=m;i++) if(fa[i]==i&&vis[i]==0) tot++;
	for(int i=2;i<=n;i++) if(!mark[i]) tot++;
	printf("%d\n",qpow(2,tot));
}