BZOJ2303: [Apio2011]方格染色

題目鏈接:http://www.lydsy.com/JudgeOnline/problem.php?id=2303

題目簡述:有一個包含n × m個方格的表格,每個方格可以塗紅色或藍色,他們想要表格中每個2 × 2的方形區域都包含奇數個(1 個或 3 個)紅色方格。有些格子已經被確定顏色,剩下的格子可以自行染色。求滿足條件的方案數MOD(10^9)。


哎這數據太弱了,,考試隨便寫了個。。神坑算法 ,就過了80分,剩下兩個點還是因爲爆棧。

首先一個沒有一個是固定的,答案就是2^(n+m-1)。這個隨便搞zhao一gui下lv就行了。記p=n+m-1

神坑算法:先看他給出來的是否有衝突,沒有衝突就找他給出來的東西的所有極大聯通塊。對每個聯通塊找一個最小的矩形把他包起來,令這個矩形的長寬爲a,b

,然後p-=(a+b-1)。。然後所有矩形搞完了就算2^p。  哎哎。。


正規算法:

其實試一下就會發現,奇數行和偶數行分別是有規律的。若第i行是奇數行且i行中第x列和第y列的顏色是一樣的,那麼所有奇數行的第x列和第y列的顏色都是一樣的。偶數行亦然。且一行只要確定了一個,整行就固定了。爲了簡化問題,所以把所有相同的關係搞到第一二行上去,並查集維護關係(相同或不同)。

再新建兩個虛擬點,一個代表紅色,一個代表藍色, 紅色藍色之間連不同!!!!!<-用來判斷衝突。!!

如果並查集加的時候有問題。那直接輸出0.

第一二行的異同情況搞出來之後,再考慮第一二行的關係,發現同一列顏色上下的關係只可能是 同 不同 同 不同 ....或者 不同 同 不同 同。

分別按兩種方法再把剛纔處理出來的並查集連上,若沒有衝突,再看有幾個集合。

若該集合和紅藍虛擬點有鏈接,說明這個集合的顏色是固定的。答案是*1 ,就是不變。

若和紅藍節點沒有連接,說明他有兩種方法(10, 01),這時答案*2。

然後把兩個情況的答案加起來。

最後還要看3-n行是不是每一行都有限制,若有一行沒有限制,那他也有兩種顏色選擇,所以答案和還要乘一個2^沒有限制的行的個數。

他的題解算法就不說了(meikandong)。但應該思路都是基本相同的。


#include <cstdio>
#include <algorithm>
typedef long long LL;
inline int getx(){
	char c;int x;
	for (c=getchar();c<'0'||c>'9';c=getchar());
	for (x=0;c>='0'&&c<='9';c=getchar())
		x=(x<<3)+(x<<1)+c-'0';
	return x;
}
struct Point{
	int x,y;short color;
	Point(int _x = 0,int _y = 0,short _c = 0):x(_x),y(_y),color(_c){}
};
bool operator <(const Point &a,const Point &b){return a.x<b.x||(a.x==b.x&&a.y<b.y);}
const LL MOD=(int)1e9;
const int MAX_N=100005;
int fa[MAX_N*2];bool delta[MAX_N*2];
int tfa[MAX_N*2];bool tdelta[MAX_N*2];
Point a[MAX_N];
int n,m,k,p;
#define black (2*m+1)
#define white (2*m+2)
//a==b 0
//a!=b 1
int getfa(int x){
	if (fa[x]!=x){
		int tp=fa[x];
		fa[x]=getfa(fa[x]);
		delta[x]=(delta[x]+delta[tp])%2;
		}
	return fa[x];
}
inline bool merge(int x,int y,int c){
	int A=getfa(x),B=getfa(y);
	if (A==B){
		int tp=(delta[x]+2-delta[y])%2;
		if (tp!=c) return false;
	}else{
		fa[A]=B;
		delta[A]=(2-delta[x]+c+delta[y])%2;
		}
	return true;
}
inline bool work(int l,int r){
	int row=(a[l].x&1)?0:m;
	for (int i=l;i<r;++i)
		if (!merge(row+a[i].y,row+a[i+1].y,a[i].color!=a[i+1].color)) return false;
	return true;
}
LL power(LL a,LL b){
	LL res=1;
	for (;b;a=a*a%MOD,b>>=1)
		if (b&1) res=res*a%MOD;
	return res;
}
bool pd[MAX_N*2];
LL ans=0;
bool work2(){
	for (int i=1;i<=2*m+2;++i) tfa[i]=fa[i],tdelta[i]=delta[i];
	bool ck1=true,ck2=true;
	int num=0;
	for (int i=1;i<=m;++i)
		if (!merge(i,m+i,(i&1)==1)){ck1=false;break;}
	if (ck1){
		for (int i=1;i<=m;++i){
			int A=getfa(i);
			if (!pd[A]){pd[A]=true;if (A!=getfa(white)&&A!=getfa(black)) num++;}
			}
		ans+=power(2,num);
		}
	num=0;
	for (int i=1;i<=2*m+2;++i) fa[i]=tfa[i],delta[i]=tdelta[i],pd[i]=0;
	for (int i=1;i<=m;++i)
		if (!merge(i,m+i,(i&1)==0)){ck2=false;break;}
	if (ck2){
		for (int i=1;i<=m;++i){
			int A=getfa(i);
			if (!pd[A]){pd[A]=true;if (A!=getfa(white)&&A!=getfa(black)) num++;}
			}
		(ans+=power(2,num))%=MOD;
		}
	return ck1||ck2;
}
int main(){
	freopen("color.in","r",stdin);
	freopen("color.out","w",stdout);
	n=getx(),m=getx(),k=getx();
	for (int i=1;i<=2*m+2;++i) fa[i]=i,delta[i]=0;
	merge(black,white,1);//這都沒加!!! 
	for (int i=1;i<=k;++i)
		a[i].x=getx(),a[i].y=getx(),a[i].color=getx();
	std::sort(a+1,a+k+1);
	int vis=0,last=1;
	for (int i=1;i<=k;++i)
	 if (a[i].x!=a[i+1].x){
		if (a[i].x<=2){
			int f=(a[i].x==1?0:m);
			for (int j=last;j<=i;++j)
				if (!merge(f+a[i].y,a[i].color?black:white,0)){printf("0");exit(0);}
			}else vis++;
		if (!work(last,i)){printf("0");exit(0);}
		last=i+1;
		}
	p=n-2-vis;
	if (!work2()) {printf("0");exit(0);}
	printf("%I64d\n",power(2,p)*ans%MOD);
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章