【51nod 1323】【JZOJ 3823】 遇见

Description

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

对于100%的数据 n<=30 Aij<=1000000000

Analysis

考虑约束:
1.每行每列都有奇数个数被选中
2.选中的数字的乘积是完全平方数
完全平方数启示我们对每个数分解质因数,指数只用保留0或1
然后可以列出方程组,设X[i,j]表示第i行第j列的数选或不选
那么对于每一行,每一列,显然是该行的X异或起来值为1
对于每个质数,那就是每格质数的指数*X异或起来值为0
于是可以解异或方程组
因为不同质数不是太多,不知道有没有1000个
高斯消元三次方看起来依然很虚,但是未知数的系数为1的很少,所以复杂度远远达不到三次方
那么答案就是 2
如何判定自由元?
如果方程已经全部用完了,但是还有许多未知数剩,那这些未知数肯定是自由元
如果某个未知数没有一个系数为1,那么X取0或1都可以,所以也是自由元
如何判定方程无解?
如果一个方程系数全为0,答案却为1,则显然出现矛盾,无解
只有这一种情况吗?是的,因为其他等价情况消元后等价于上述情况

Code

#include<cstdio>
#include<cmath>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,b,a) for(int i=b;i>=a;i--)
#define num(i,j) ((((i)-1)*n)+(j))
using namespace std;
const int N=35,mo=1e9+7;
int n,m,c[N][N],p[5000],pri[10000],a[5000][N][N];
int mat[5065][N*N];
long long ans;
void prepare()
{
    scanf("%d",&n);
    fo(i,1,n)
        fo(j,1,n)
        {
            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);
    fo(i,1,pri[0])
        if(pri[i]!=pri[i-1]) p[++p[0]]=pri[i];
    fo(i,1,n)
        fo(j,1,n)
        {
            int x=c[i][j];
            fo(k,1,p[0])
                while(x%p[k]==0) a[k][i][j]^=1,x/=p[k];
        }
}
void makemat()
{
    fo(i,1,n)
    {
        mat[i][0]=1;
        fo(j,1,n) mat[i][num(i,j)]=1;
    }
    fo(i,1,n)
    {
        mat[n+i][0]=1;
        fo(j,1,n) mat[n+i][num(j,i)]=1;
    }
    m=n+n;
    fo(k,1,p[0])
    {
        mat[++m][0]=0;
        fo(i,1,n)
            fo(j,1,n) mat[m][num(i,j)]=a[k][i][j];
    }
}
void gauss()
{
    int num=0;
    ans=1;
    int pos=1;
    fo(i,1,n*n)
    {
        bool q=0;
        fo(j,pos,m)
            if(mat[j][i])
            {
                swap(mat[pos],mat[j]),q=1;
                break;
            }
        if(!q) num++;
        else pos++;
        fo(j,pos,m)
            if(mat[j][i])
                fo(k,0,n*n) mat[j][k]^=mat[pos-1][k];
    }
    fo(i,1,m)
    {
        bool q=0;
        fo(j,1,n*n)
            if(mat[i][j]) {q=1;break;}
        if(q) continue;
        if(mat[i][0]) ans=0;
    }
    fo(i,1,num) ans=ans*2%mo;
}
int main()
{
    prepare();
    makemat();
    gauss();
    printf("%lld\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值