• bzoj 2303 并查集


      首先如果没有限制的话,我们可以直接求出答案,假设对于n*m的矩阵,我们最上方一行和左方的一列随意确定,那么首先这写确定的状态肯定是不会不合法的,因为我们可以调整剩下的01状态来使得这一行一列的状态合法,而且剩下的01状态唯一确定,我们叫最上面的行和左面的列为标准行列,那么我们对于每一种不同的标准行列都可以有一组合法解,那么最后答案的数量就是标准行列的数量,那么标准行列一共有2^(n+m-1)种方案,那么这就是最后的答案。

      那么每一个四方阵的1个数为奇数代表这个四方阵的xor和为1,那么根据xor的结合律,我们可以得到一个式子,假设a[i][j]为合法方案中[i][j]位置的数,那么a[1][1]^a[i][j]^a[1][j]^a[i][1]=0或1,当且仅当i,j都为偶数的时候等于0,那么有了这个性质我们可以根据给出的限制判断[1][j]和[i][1]位置的01选择是否相同,那么我们可以将标准行列的每一个点看成两个点,即这个位置选的是0还是1,那么根据给出的限制我们可以知道在这个点选0或1时其余某些点必须选0或1,那么这就是一个简单的2-sat模型了,我们可以连出图之后判断各个连通块中是否有不同的颜色(即题目直接对标准行列做限制),有的话即无解,那么我们可以确定出独立点的个数,即这些点的颜色确定不会对其他点造成影响。

      对于左上角选1的情况我们只需要把限制中的01状态全部取xor然后再按照上述方法做一遍就可以了。

      那么在实际操作的时候我们可以用并查集来维护及节点之间的信息,设father[x]为x的父亲节点,ww[x]为x与其父亲节点的关系,ww[x]为0时代表颜色相同,1时则不同,那么我们只需要维护这个就可以了。

      备注:在合并a,b的时候,设fa为a的父亲应该ww[fa]=ww[a]+ww[b]+a,b节点之间的关系(为01),结果开始的时候忘了ww[a]了,改了之后又把ww[b]删了,查了半天= =。

    /**************************************************************
        Problem: 2303
        User: BLADEVIL
        Language: C++
        Result: Accepted
        Time:1340 ms
        Memory:71120 kb
    ****************************************************************/
     
    //By BLADEVIL
    #include <cstdio>
    #include <cstring>
    #define maxn 3000010
    #define d39 1000000000
    #define LL long long
     
    using namespace std;
     
    struct rec {
        int x,y,z;
    }ask[maxn];
     
    int n,m,N,k,w,ans;
    int flag[maxn],father[maxn],ww[maxn];
     
    int getfather(int x) {
        if (father[x]==x) return x;
        int fa=father[x];
        father[x]=getfather(fa);
        ww[x]=(ww[x]+ww[fa])%2;
        return father[x];
    }
     
    int pwr(int x,int k) {
        int ans=1;
        while (k) {
            if (k&1) ans=(LL)ans*x%d39;
            k>>=1;
            x=(LL)x*x%d39;
        }
        return ans;
    }
     
    int calc() {
        memset(flag,0,sizeof flag);
        memset(ww,0,sizeof ww);
        N=n+m-2;
        for (int i=1;i<=N;i++) father[i]=i; 
        for (int i=1;i<=k;i++) {
            if (ask[i].x==1) {
                int cur;
                if (ask[i].z) cur=3; else cur=2;
                if (flag[ask[i].y-1]) {
                    if (flag[ask[i].y-1]!=cur) return 0; else flag[ask[i].y-1]=cur;
                } else flag[ask[i].y-1]=cur;
                continue;
            }
            if (ask[i].y==1) {
                int cur;
                if (ask[i].z) cur=3; else cur=2;
                if (flag[ask[i].x+m-2]) {
                    if (flag[ask[i].x+m-2]!=cur) return 0; else flag[ask[i].x+m-2]=cur;
                } else flag[ask[i].x+m-2]=cur;
                continue;
            }
            int a=ask[i].y-1,b=ask[i].x+m-2;
            //printf("%d %d
    ",a,b);
            int fa=getfather(a),fb=getfather(b),add=0;  
            if ((ask[i].x%2==0)&&(ask[i].y%2==0)) {
                if (ask[i].z) add=0; else add=1;
            } else {
                if (ask[i].z) add=1; else add=0;
            }
            //printf("%d
    ",add);
            if (fa==fb) {
                int cur=(ww[a]+ww[b])%2;
                if (cur!=add) return 0;
            } else father[fa]=fb; ww[fa]=(ww[b]+ww[a]+add)%2;
            //for (int i=1;i<=N;i++) printf("|%d %d %d %d
    ",i,father[i],ww[i],flag[i]);
        }
        //for (int i=1;i<=N;i++) printf("|%d %d %d
    ",i,father[i],ww[i]);
        //for (int i=1;i<=N;i++) printf("%d ",flag[i]); printf("
    ");
        int cnt=0;
        for (int i=1;i<=N;i++) if (father[i]==i) cnt++;
        //printf("%d ",cnt);
        for (int i=1;i<=N;i++) if (flag[i]) {
            if (!flag[getfather(i)]) flag[getfather(i)]=(flag[i]+ww[i])%2+2; else
            if (flag[getfather(i)]!=(flag[i]+ww[i])%2+2) return 0;
        }
        //for (int i=1;i<=N;i++) printf("%d ",flag[i]); printf("
    ");
        for (int i=1;i<=N;i++) if ((father[i]==i)&&(flag[i])) cnt--;
        //printf("%d
    ",cnt);
        return pwr(2,cnt);
    }
     
    int main() {
        scanf("%d%d%d",&n,&m,&k);
        for (int i=1;i<=k;i++) scanf("%d%d%d",&ask[i].x,&ask[i].y,&ask[i].z);
        for (int i=1;i<=k;i++) if ((ask[i].x==1)&&(ask[i].y==1)) w=i;
        //calc(); return 0;
        if (w) {
            if (ask[w].z)
                for (int i=1;i<=k;i++) ask[i].z^=1;
            ans=calc();
        } else {
            ans=calc()%d39;
            for (int i=1;i<=k;i++) ask[i].z^=1;
            (ans+=calc())%=d39;
        }
        printf("%d
    ",ans);
        return 0;
    }
  • 相关阅读:
    如何提高团队开发质量
    settimeout promise
    一大波开源小抄来袭
    西瓜播放器 字节跳动出品的开源 H5 视频组件,带解析器、节省流量
    如何参与开源项目 细说 GitHub 上的 PR 全过程
    alifd的dialog.show的源代码 ReactDOM.render
    竟然有一半的人不知道 for 与 foreach 的区别???
    适配器模式在 MyBatis 中的妙用,面试可以拿来吹了!
    图文并茂详解 Git,看了必懂!
    监控界的最强王者,没有之一!
  • 原文地址:https://www.cnblogs.com/BLADEVIL/p/3687255.html
Copyright © 2020-2023  润新知