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;
}