【BZOJ 3503】 [Cqoi2014]和谐矩阵

3503: [Cqoi2014]和谐矩阵

Time Limit: 10 Sec Memory Limit: 128 MBSec Special Judge

Submit: 493 Solved: 216

[Submit][Status][Discuss]

Description

我们称一个由0和1组成的矩阵是和谐的,当且仅当每个元素都有偶数个相邻的1。一个元素相邻的元素包括它本

身,及他上下左右的4个元素(如果存在)。

给定矩阵的行数和列数,请计算并输出一个和谐的矩阵。注意:所有元素为0的矩阵是不允许的。

Input

输入一行,包含两个空格分隔的整数m和n,分别表示矩阵的行数和列数。

Output

输出包含m行,每行n个空格分隔整数(0或1),为所求矩阵。测试数据保证有解。

Sample Input

4 4

Sample Output

0100

1110

0001

1101

数据范围

1 <=m, n <=40

HINT

Source

鸣谢Ljcc提供Spj

高斯消元解异或方程组(具体做法见【POJ 1222】)。

有两种做法:

①速度较慢的方法:

对于矩阵中的每一个元素都可以列出一个方程,m?n个未知数,m?n个方程(f[i][j]表示第i个对第j个有影响):

a[k]+f[1][k]?a[1]+f[2][k]?a[2]+...f[tot][k]?a[tot]=0(mod2)

#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdio>
#include <cstdlib>
using namespace std;
int a[2000][2000],ans[2000],d[10][5],id[50][50],n,m;
void Gauss(int n,int m)
{
    for (int i=1;i<=n;i++)
    {
        int j;
        for (j=i;j<=m&&!a[j][i];j++);
        if (j>n) continue;
        if (j!=i)
            for (int k=i;k<=m;k++)
                swap(a[i][k],a[j][k]);
        for (int j=i+1;j<=n;j++)
            if (a[j][i])
                for (int k=i;k<=m;k++)
                    a[j][k]^=a[i][k];
    }
    for (int i=n;i;i--)
    {
        if (!a[i][i])
        {
            ans[i]=1;
            continue;
        }
        for (int j=i+1;j<=n;j++)
            a[i][m]^=(ans[j]*a[i][j]);
        ans[i]=a[i][m];
    }
}
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    d[0][1]=d[0][2]=0;
    d[1][1]=d[2][1]=0,d[1][2]=1,d[2][2]=-1;
    d[3][2]=d[4][2]=0,d[3][1]=1,d[4][1]=-1;
    int cnt=0;
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++)
            id[i][j]=++cnt;
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++)
            for (int k=0;k<=4;k++)
            {
                int p=id[i+d[k][1]][j+d[k][2]];
                if (p) a[id[i][j]][p]=1;
            }
    Gauss(cnt,cnt+1);
    cnt=0;
    for (int i=1;i<=n;i++)
    {
        printf("%d",ans[++cnt]);
        for (int j=2;j<=m;j++)
            printf(" %d",ans[++cnt]);
        printf("\n");
    }
    return 0;
}

②速度快的办法:

可以发现当第一行确定之后,整个矩阵就确定了,因此我们只要把第一行设为未知数即可。

如何判断第一行的某种排列是否合题意?

如果第m+1行全部是0就合题意,否则不合。

而第m+1行的每一个数一定是第一行的某些数异或起来的,我们只要只要确定他是由哪些数异或得到(求出关系数组f[i][j]),使这些数的异或值为0即可。

最终列出n个方程,有n个未知数。(orz zyf-zyf)

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#define LL long long
using namespace std;
LL b[45][45];
int a[45][45],ans[45][45],n,m;
void Gauss()
{
    for (int i=1;i<=m;i++)
    {
        int j;
        for (j=i;j<=m&&!a[j][i];j++);
        if (j>m) continue;
        if (j!=i)
            for (int k=i;k<=m+1;k++)
                swap(a[i][k],a[j][k]);
        for (int j=i+1;j<=m;j++)
            if (a[j][i])
                for (int k=i;k<=m+1;k++)
                    a[j][k]^=a[i][k];
    }
    for (int i=m;i;i--)
    {
        if (!a[i][i])
        {
            ans[1][i]=1;
            continue;
        }
        for (int j=i+1;j<=m;j++)
            a[i][m+1]^=(ans[1][j]*a[i][j]);
        ans[1][i]=a[i][m+1];
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=m;i++)
        b[1][i]=(LL)1<<(i-1);
    for (int i=2;i<=n+1;i++)
        for (int j=1;j<=m;j++)
            b[i][j]=b[i-1][j-1]^b[i-1][j]^b[i-1][j+1]^b[i-2][j];
    for (int i=1;i<=m;i++)
        for (int j=1;j<=m;j++)
            a[i][j]=(b[n+1][i]>>(j-1))&1;
    Gauss();
    for (int i=1;i<=m;i++)
        printf("%d ",ans[1][i]);
    printf("\n");
    for (int i=2;i<=n;i++)
    {
        for (int j=1;j<=m;j++)
        {
            ans[i][j]=ans[i-1][j-1]^ans[i-1][j]^ans[i-1][j+1]^ans[i-2][j];
            printf("%d ",ans[i][j]);
        }
        printf("\n");
    }
    return 0;
}

感悟:

高斯消元解异或方程组:方程中异或相当于相加,于是异或方程就变成了普通的方程;

列异或方程通常要先求出关系数组,给每个数乘上他们的关系,1表示有影响,0表示没有影响。

时间: 2024-08-04 07:47:37

【BZOJ 3503】 [Cqoi2014]和谐矩阵的相关文章

【高斯消元】BZOJ3503 [Cqoi2014]和谐矩阵

3503: [Cqoi2014]和谐矩阵 Time Limit: 10 Sec  Memory Limit: 128 MBSec  Special JudgeSubmit: 1197  Solved: 570[Submit][Status][Discuss] Description 我们称一个由0和1组成的矩阵是和谐的,当且仅当每个元素都有偶数个相邻的1.一个元素相邻的元素包括它本 身,及他上下左右的4个元素(如果存在). 给定矩阵的行数和列数,请计算并输出一个和谐的矩阵.注意:所有元素为0的矩

BZOJ_3503_[Cqoi2014]和谐矩阵_高斯消元

题意: 我们称一个由0和1组成的矩阵是和谐的,当且仅当每个元素都有偶数个相邻的1.一个元素相邻的元素包括它本身,及他上下左右的4个元素(如果存在).给定矩阵的行数和列数,请计算并输出一个和谐的矩阵.注意:所有元素为0的矩阵是不允许的. 分析: 考虑一种暴力,设n*m个未知数,列n*m个方程 高斯消元解方程,注意全零矩阵不合法 那我们如果发现有自由元就将它们置为1 代码: #include <stdio.h> #include <string.h> #include <algo

Luogu3164 CQOI2014 和谐矩阵 异或、高斯消元

传送门 题意:给出$N,M$,试构造一个$N \times M$的非全$0$矩阵,其中所有格子都满足:它和它上下左右四个格子的权值之和为偶数.$N , M \leq 40$ 可以依据题目中的条件列出有$N \times M$的元.$N \times M$个方程的异或方程组(异或方程组就是所有位置都是$1$或$0$,最右边一列的答案需要通过异或互相消除的方程组,一般在$mod\,2$意义下产生). 理论上元和方程组数量一致的时候每一个元都是有唯一解的,但是在有解的情况下,其中一些方程是线性相关的,

BZOJ3503: [Cqoi2014]和谐矩阵

题解: 如果第一行的数知道了,我们就可以推出其他行的数. 那么如何判断第一行的数的一种填法是否合法呢?很简单,我们递推出m+1行的数,当且仅当这一行都是0时满足题意. 那么,我们就有了一种想法. 直接把m+1行的每个数用x[1..n]表示出来,这一定是个系数只为0/1的式子.然后让这个异或值=0,就可以解异或方程组了. 系数怎么推呢? for1(i,n)b[1][i]=(ll)1<<i-1; for2(i,2,m+1) for1(j,n) b[i][j]=b[i-1][j]^b[i-1][j-

【BZOJ】【3503】【CQOI2014】和谐矩阵

高斯消元解Xor方程组 Orz ZYF o(︶︿︶)o 唉我的数学太烂了…… 错误思路:对每个格点进行标号,然后根据某5个异或和为0列方程组,高斯消元找自由元……(目测N^3会TLE) ZYF的正确思路: 如果第一行的数知道了,我们就可以推出其他行的数. 那么如何判断第一行的数的一种填法是否合法呢?很简单,我们递推出m+1行的数,当且仅当这一行都是0时满足题意. 那么,我们就有了一种想法. 直接把m+1行的每个数用x[1..n]表示出来,这一定是个系数只为0/1的式子.然后让这个异或值=0,就可

BZOJ 3503 CQOI 2014 和谐矩阵 高斯消元

题目大意:给出m和n,求出一种方案使得每一个点和周围的四个点的1的个数为偶数. 思路:根据题意可以列出m*n个异或方程,然后组成异或方程组.解这个异或方程组然后输出任意一个解就可以了. PS:值得注意的是,全是0肯定是一个解,显然你不能输出这个解.所以你需要让一个或一些自由元的值为1,至于怎么做,随便yy就行了. PS2:这个题的样例吞掉了空格,然而又是SPJ,所以就是wa..然后我wa了一下午.. CODE: #include <cstdio> #include <cstring>

【BZOJ3503】【Cqoi2014】和谐矩阵 高斯消元,解异或方程组

#include <stdio.h> int main() { puts("转载请注明出处"); puts("地址:blog.csdn.net/vmurder/article/details/43699831"); } 题解: 随便搞搞就好. 自由元全当成1就好了么~~~ 不会异或方程组的移步这里[POJ1222]EXTENDED LIGHTS OUT 高斯消元.解异或方程组 代码: #include <cstdio> #include &l

[BZOJ 1048] [HAOI2007] 分割矩阵 【记忆化搜索】

题目链接:BZOJ - 1048 题目分析 感觉这种分割矩阵之类的题目很多都是这样子的. 方差中用到的平均数是可以直接算出来的,然后记忆化搜索 Solve(x, xx, y, yy, k) 表示横坐标范围 [x, xx], 纵坐标范围 [y, yy] 的矩阵切成 k 块的最小 sigma((Vi - Ave)^2) . 然后再递归将矩阵分得更小,直到 k 为 1 的时候直接返回相应的值. 代码 #include <iostream> #include <cstdlib> #incl

BZOJ 3505: [Cqoi2014]数三角形( 组合数 )

先n++, m++ 显然答案就是C(3, n*m) - m*C(3, n) - n*C(3, m) - cnt. 表示在全部点中选出3个的方案减去不合法的, 同一行/列的不合法方案很好求, 对角线的不合法方案cnt比较麻烦. 枚举对角线(左下-右上), 即(0, 0)-(x, y), 我们发现这种情况有(n-y)*(m-x)*2(算上左上-右下的)种, 然后中间有gcd(x, y)-1个点(不合法), 乘起来就好了. ---------------------------------------