51nod 1323 完美平方 线性基+bitset+数学

24 篇文章 0 订阅
14 篇文章 0 订阅

题意

有一个N*N的矩阵M,其中包含N*N正整数。现在要从这个矩阵中选出一些元素来,要求:
1)每行恰好选择了奇数个元素;
2)每列恰好选择了奇数个元素;
3)选出的所有元素的乘积是个完全平方数;
4)每个元素不能重复选择,即你不能选择M(i,j)多次。
问有多少种不同的选取方法。输出方法数mod 1,000,000,007后的答案。
1<=N<=20,1<=M[i][j]<=1,000,000,000。

分析

一开始没有往线性方程组上想,而是想的各种状压各种dp。。。

其实这题我们可以把每个数的素因子筛出来,算了算400个数大概会出现1000种不同的素因数。对每行每列和每个素因数都建立一个异或方程,然后对系数矩阵进行消元,最后答案就是2^(n*n-矩阵的秩)。因为有n*n个变量,只有(矩阵的秩)那么多个方程,那么自由元自然就有(n*n-矩阵的秩)那么多个啦。
因为方程有点多,可以用bitset来优化。
一开始写的高斯消元,答案不知道怎么的总会多1,后来改成用线性基来消元就过了。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<bitset>
using namespace std;

const int N=25;
const int MOD=1000000007;

int n,pri[N][N][35],tot[N][N],w[10005];
bitset<405> a[1505],bas[405];

int read()
{
    int x=0,f=1;char ch=getchar();
    while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

int point(int x,int y)
{
    return (x-1)*n+y;
}

int gauss(int n,int m)
{
    for (int i=1;i<=n;i++) bas[i].reset();
    int s=0;
    for (int i=1;i<=m;i++)
    {
        for (int j=n;j>=1;j--)
            if (a[i][j])
            {
                if (!bas[j].count()) {bas[j]=a[i];s++;break;}
                else a[i]^=bas[j];
            }
        if (a[i].count()==1&&a[i][n+1]) return -1;
    }
    return s;
}

int main()
{
    int T=read();
    while (T--)
    {
        n=read();
        int w1=0;
        for (int i=1;i<=n;i++)
            for (int j=1;j<=n;j++)
            {
                int x=read();tot[i][j]=0;
                for (int k=2;k*k<=x;k++)
                    if (x%k==0)
                    {
                        int s=0;
                        while (x%k==0) x/=k,s^=1;
                        if (s) pri[i][j][++tot[i][j]]=k,w[++w1]=k;
                    }
                if (x>1) pri[i][j][++tot[i][j]]=x,w[++w1]=x;
            }
        sort(w+1,w+w1+1);w1=unique(w+1,w+w1+1)-w-1;
        for (int i=1;i<=n*2+w1;i++) a[i].reset();
        for (int i=1;i<=n;i++)
        {
            a[i][n*n+1]=a[i+n][n*n+1]=1;
            for (int j=1;j<=n;j++)
            {
                a[i][point(i,j)]=a[j+n][point(i,j)]=1;
                for (int k=1;k<=tot[i][j];k++)
                {
                    int id=lower_bound(w+1,w+w1+1,pri[i][j][k])-w;
                    a[id+n*2][point(i,j)]=1;
                }
            }
        }
        int s=gauss(n*n,n*2+w1),ans=1;
        if (s==-1) puts("0");
        else
        {
            for (int i=1;i<=n*n-s;i++) ans=ans*2%MOD;
            printf("%d\n",ans);
        }
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值