BZOJ 2303 数学分析+并查集

来源:互联网 发布:关于51单片机与esp8266 编辑:程序博客网 时间:2024/06/04 17:59

       

    首先对于没有提前染色的情况,方案数应当是2^(n+m-1),为什么呢,因为对于给定的第一行和第一列来说,染色的方法是唯一确定的,因为我们可以从染好3个格的2*2方格内推出第4个格的颜色,这样我们就能够利用推出的第4个格继续推,直到所有格都被推出来。因此每有一种第一行和第一列的染色方式,就会有一个唯一的染色方式与之对应。而现在题目中加入了一些提前染好色的格子,又该怎么办呢?

    我们把红色定义成1,蓝色定义成0,可以发现一个2*2的方格满足题目中的条件当且仅当这些格子的XOR和等于1,我们再利用XOR的性质将其推而广之,可以发现这样一个性质:一个长为i,宽为j的矩形合法,当且仅当i和j全部是偶数时,矩形的4个顶点XOR和为1,其余情况均为0,这样我们就可以扫一遍每一个染色的格子,先假设a[1][1]=1,这样我们就能够知道剩余2个格子的关系,即他们是同一个颜色还是不同颜色,我们可以用并查集来维护,可以知道剩余两个格子一定是在第一行或第一列,当第一行或第一列的某个格子被染色后,那么以它为根节点的与它相连的所有格子的颜色都唯一确定,答案就是2的(块数-唯一确定的块数)次幂,因为每一块都必须染同种颜色,而这种颜色可能是红色或蓝色,有两种情况,有块数那么多个,根据乘法原理即可推出答案。

    最后需要注意判断一下无解的情况,这些都是在并查集中做的事了,不理解的可以看下面的代码

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;#define maxn 1000006#define mod 1000000000int xx[maxn],yy[maxn],col[maxn],father[maxn];int n,m,k,w,flag[maxn],type[maxn],ans;int power(long long x,int y){long long ans=1;for (;y;y>>=1) {if (y&1) ans=(ans*x)%mod;x=(x*x)%mod;}return (int)ans;}int getfather(int x){int temp=father[x];if (x!=father[x]) father[x]=getfather(father[x]);flag[x]=(flag[x]+flag[temp])%2;return father[x];}int solve(void) {int N=n+m-2;for (int i=1;i<=N;i++) father[i]=i;memset(flag,0,sizeof flag);memset(type,0,sizeof type);for (int i=1;i<=k;i++) {if (xx[i]==1) {int u=yy[i]-1;int cur;if (col[i]) cur=3;else cur=2;if (type[u]==0) type[u]=cur;if (type[u]!=cur) return 0;continue;}if (yy[i]==1){int u=xx[i]+m-2;int cur;if (col[i]) cur=3;else cur=2;if (type[u]==0) type[u]=cur;if (type[u]!=cur) return 0;continue;}int a=xx[i]+m-2,b=yy[i]-1;int fa=getfather(a),fb=getfather(b),add;if (xx[i]%2==0&&yy[i]%2==0) if (col[i]) add=0;else add=1;else if (col[i]) add=1;else add=0;if (fa==fb) {if ((flag[b]-flag[a]+2)%2!=add) return 0;}else {father[fb]=fa;flag[fb]=(flag[a]+add-flag[b]+2)%2;}}int cnt=0;for (int i=1;i<=N;i++) if (father[i]==i) cnt++;for (int i=1;i<=N;i++) if (type[i]) {int ft=getfather(i);if (type[ft]){if (type[ft]!=(type[i]+flag[i])%2+2) return 0;}else type[ft]=(type[i]+flag[i])%2+2; }for (int i=1;i<=N;i++) if (father[i]==i&&type[i]) cnt--;return power((long long)2,cnt);}int main(){scanf("%d%d%d",&n,&m,&k);for (int i=1;i<=k;i++) scanf("%d%d%d",xx+i,yy+i,col+i);for (int i=1;i<=k;i++) if (xx[i]==1&&yy[i]==1) w=i;if (w) {if (col[w]) for (int i=1;i<=k;i++) col[i]^=1;ans=solve();}else {ans=solve()%mod;for (int i=1;i<=k;i++) col[i]^=1;ans=(ans+solve())%mod;}printf("%d\n",ans);return 0;}


0 0
原创粉丝点击