【NOIP2014模拟9.9】遇见

Description

Zyh独自一人在街上漫步。Zyh相信不久后应该就可以和她一起漫步,可是去哪里寻找那个她呢?Zyh相信每个人都有一个爱情的号码牌,这个号码牌是一个n*n的矩阵。
每个人都要在矩阵中选择若干个元素,使得每行每列都有奇数个数被选中,且选中的数字的乘积是完全平方数。每当选出了这若干个元素,他/她就能找到那个她/他。
Zyh想知道对于一个号码牌有多少种选择的方法,使得zyh能够不再孤独。由于这个数字很大,只要输出对1,000,000,007取模后的余数即可。

Input

第一行是一个数正整数n。接下来是n行n列的矩阵。

Output

一个数,即方案数对于1000000007取模后的余数。

Sample Input

输入1:

2
1 1
1 2

输入2:

2
620 620
620 620

Sample Output

输出1:

1

输出2:

2

Data Constraint

第一类:对于30%的数据 n<=4 Aij<=10
第二类:对于50%的数据 n<=10 n*n个数分解质因数后的不同素数个数不超过5个
第三类:对于80%的数据 n<=15 Aij<=1000000000
第四类:对于100%的数据 n<=30 Aij<=1000000000
在第三类(不属于第二类)的数据中 有10%的数据满足Aij都为同一个素数

思路

高斯消元。
本蒟蒻第一次打高斯消元

首先,题目叫我们凑成完全平方数,所以果断分解质因数,保留指数奇偶。

然后可以列出方程组,设X[i,j]表示第i行第j列的数选或不选
那么对于每一行,每一列,显然是该行的X异或起来值为1
对于每个质数,那就是每格质数的指数*X异或起来值为0
于是可以解异或方程组 。

于是,高斯消元即可。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define num(i,j) (((i-1)*n)+(j))
using namespace std;
const int N=40,mo=1000000007;
int n,m,c[N][N],p[5555],pri[11111],a[5555][N][N];
int f[5555][N*N];
long long ans;
void init()
{
    scanf("%d",&n);
    for(int i=1; i<=n; i++)
        for(int j=1; j<=n; j++)
        {
            scanf("%d",&c[i][j]);
            int x=c[i][j];
            for(int k=2;k*k<=x;k++)
                if(x%k==0)
                {
                    pri[++pri[0]]=k;
                    while(x%k==0) x/=k;
                }
            if(x>1) pri[++pri[0]]=x;
        }
    sort(pri+1,pri+pri[0]+1);
    for(int i=1; i<=pri[0]; i++)
        if(pri[i]!=pri[i-1]) p[++p[0]]=pri[i];
    for(int i=1; i<=n; i++)
        for(int j=1; j<=n; j++)
        {
            int x=c[i][j];
            for(int k=1; k<=p[0]; k++)
                while(x%p[k]==0) a[k][i][j]^=1,x/=p[k];
        }
}
void make()
{
    for(int i=1; i<=n; i++)
    {
        f[i][0]=1;
        for(int j=1; j<=n; j++) f[i][num(i,j)]=1;
    }
    for(int i=1; i<=n; i++)
    {
        f[n+i][0]=1;
        for(int j=1; j<=n; j++) f[n+i][num(j,i)]=1;
    }
    m=n+n;
    for(int k=1; k<=p[0]; k++)
    {
        f[++m][0]=0;
        for(int i=1; i<=n; i++)
            for(int j=1; j<=n; j++) f[m][num(i,j)]=a[k][i][j];
    }
}
void doit()
{
    int num=0;
    ans=1;
    int pos=1;
    for(int i=1; i<=n*n; i++)
    {
        bool q=0;
        for(int j=pos; j<=m; j++)
            if(f[j][i])
            {
                swap(f[pos],f[j]),q=1;
                break;
            }
        if(!q) num++;
        else pos++;
        for(int j=pos; j<=m; j++)
            if(f[j][i])
                for(int k=0; k<=n*n; k++) f[j][k]^=f[pos-1][k];
    }
    for(int i=1; i<=m; i++)
    {
        bool q=0;
        for(int j=1; j<=n*n; j++)
            if(f[i][j]) {q=1;break;}
        if(q) continue;
        if(f[i][0]) ans=0;
    }
    for(int i=1; i<=num; i++) ans=ans*2%mo;
}
int main()
{
    init();
    make();
    doit();
    printf("%lld\n",ans);
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值