您的位置:首页 > 其它

【BZOJ2303】【Apio2011】方格染色 异或方程+并查集

2015-04-16 21:52 399 查看

链接:

#include <stdio.h>
int main()
{
    puts("转载请注明出处[vmurder]谢谢");
    puts("网址:blog.csdn.net/vmurder/article/details/45081583");
}


题解:

首先我们发现对于 ai,ja_{i,j} 有下列式子:

ai,j xor ai+1,j xor ai,j+1 xor ai+1,j+1==1a_{i,j} ~xor\ a_{i+1,j} ~xor\ a_{i,j+1} ~xor\ a_{i+1,j+1} == 1

然后推导得到对于 ai,ja_{i,j} 有下列式子:

a1,1 xor a1,j xor ai,1 xor ai,j==!(i,j均为偶数)a_{1,1} ~xor\ a_{1,j} ~xor\ a_{i,1} ~xor\ a_{i,j} == !(i,j均为偶数)

然后显然如果我们先涂上第一行和第一列,剩下的点的染色方案是唯一的。

而这些已经的填涂 (i,j)(i,j) 则成了 (1,j)(1,j) 和 (i,1)(i,1) 这俩点之间的限制,即方程四点异或和确定。

我们可以先枚举 (1,1)(1,1) 是 00 还是 11 ,然后其它的染色作为点之间关系跑并查集。

然后会剩下 xx 个连通块,刨去 11 的一个,则此种对 11 染色的方案(染0或者染1)的答案贡献就是 2x−12^{x-1} 。

实现:

一些定义:

一个被提前染色了的点有染色权值为其染的色,而如果这个点横纵坐标皆为偶数,则权值取反。

首先说左上角为 00 时的答案计算:

对于点 (i,j),i>1且j>1(i,j) ,i >1且j>1 则它是 点 ii 和 点j+nj+n 之间的限制,表示这两点之间差异为其染色权值。

【原因:如果权值为1,左上角还是0,那么俩点最终染色必须不一样对吧,反之亦然】

对于其它情况,即点在第一行或第一列上,我们认为这是在表示此点和点 (1,1)(1,1) 的关系,处理方式可以与上一种情况相同(甚至在我的实现下代码也是相同哒,不用特殊考虑!)

【原因:如果权值为1,表示与 (0,0)(0,0) 目前 “0”“0” 的染色不同。】

然后对于左上角是 11 时的答案计算:

我们只需要把所有点的染色权值取反,然后推一下发现正好可以跟上述做相同处理。

也就是直接调用上述流程就可以啦!!

特殊问题:

如果点 (1,1)(1,1) 被染色,那么需要特殊判定上面对 11 的枚举。

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 1001000
#define mod 1000000000
#define A e[i].x
#define B e[i].y+n
#define C e[i].z
using namespace std;
struct Eli
{
    int x,y,z;
    bool read()
    {
        scanf("%d%d%d",&x,&y,&z);
        if(x==1&&y==1)return 1;
        if(!((x|y)&1))z^=1;
        return 0;
    }
}e
;
int f[N<<1];
bool g[N<<1];
int find(int x)
{
    if(f[x]==x)return x;
    find(f[x]);
    g[x]^=g[f[x]];
    return f[x]=f[f[x]];
}
long long power(long long x,long long p)
{
    long long ret=1;
    while(p)
    {
        if(p&1)ret=(ret*x)%mod;
        x=(x*x)%mod,p>>=1;
    }
    return ret;
}
int n,m,p;
long long getans()
{
    int i,fa,fb,t;
    for(i=1;i<=n+m;i++)f[i]=i,g[i]=0;
    f[n+1]=1;
    for(i=1;i<=p;i++)
    {
        fa=find(A),fb=find(B),t=g[A]^g[B]^C;
        if(fa!=fb)f[fb]=fa,g[fb]=t;
        else if(t)return 0;
    }
    long long ans=0;
    if(i>p)for(ans=0,i=1;i<=n+m;i++)
        if(find(i)==i)ans++;
    return power(2,ans-1);
}
bool f1=1,f2=1;
long long ans1,ans2;
int main()
{
    scanf("%d%d%d",&n,&m,&p);
    for(int i=1;i<=p;i++)
    {
        if(e[i].read())
        {
            if(e[i].z)f1=0;
            else f2=0;
            i--,p--;
        }
    }

    if(f1)ans1=getans();
    if(f2)
    {
        for(int i=1;i<=p;i++)
            if(e[i].x>1&&e[i].y>1)e[i].z^=1;
        ans2=getans();
    }

    cout<<(ans1+ans2)%mod<<endl;
    return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: