【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】)。

有两种做法:
①速度较慢的方法:
对于矩阵中的每一个元素都可以列出一个方程, mn 个未知数, mn 个方程( 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表示没有影响。

你可能感兴趣的:(高斯消元,OI,bzoj)