您的位置:首页 > 其它

BZOJ2303: [Apio2011]方格染色

2014-02-28 10:02 253 查看
题目链接: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);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  combinatorics