【JZOJ 3823】【NOIP2014模拟9.9】遇见

Description

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

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都为同一个素数

Solution

这是我第一次打高斯消元,可一上来就是一道高斯消元异或方程组……
我们可以把所有约束变成一道道异或方程。
1、对于每行每列都有奇数个数被选中的约束。我们可以把第i行的每个未知数配上一个系数1。举个例子,第i行的式子为 x(i1)n+1 ^ x(i1)n+2 ^……^ xin==1 。列的情况类似。
2、对于最终要求完全平方,我们可以拆解成n个质因数的指数为偶数。所以我们可以将n*n个数每个未知数的系数为它最多是该质因数的几次幂的倍数,例如36就是2的2次幂的倍数,同时36是3的2次幂的倍数。每个质因数也同样可以列出一个式子。
最后只要将这些高斯消元一下即可。

一些小问题

1、要注意,我们找不到当前第i位为1的式子时,我们可以将一条无意义式子(即类似0^0^0^……^0=0)移上第i位。
2、式子有可能无解。当最后你发现存在式子(类似0^0^0^……^0=1)时,则为无解。因为你不能什么都不选。

Code

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn=2005,maxn1=35,mo=1e5+5,mo1=1e9+7;
int f[maxn][1000],n,i,t,j,k,l,a[maxn1][maxn1],num,h[mo],b[maxn],x,y,c[maxn],d[maxn],ans,p;
void hash(int x){
    int t=x%mo;
    while (h[t]!=x && h[t]) t=(t+1)%mo;
    if (!h[t]) h[t]=x,b[++b[0]]=x;
}
ll mi(int x){
    if (x==1) return 2;if (!x) return 1;
    ll t=mi(x/2);
    if (x%2) return t*t%mo1*2%mo1;return t*t%mo1;
}
int main(){
//  freopen("data.in","r",stdin);
    scanf("%d",&n);
    for (i=1;i<=n;i++)
        for (j=1;j<=n;j++)
            scanf("%d",&a[i][j]);
    t=a[1][1];k=0;
    for (i=1;i<=n;i++)
        for (j=1;j<=n;j++)
            if (a[i][j]==t) k++;
    if (k==n*n && t>1 && (n%2)){
        printf("0\n");
        return 0;
    }
    for (i=1;i<=n;i++){
        for (j=(i-1)*n+1;j<=i*n;j++)
            f[i][j]=1;
        f[i][n*n+1]=1;
    }
    num=n;
    for (i=1;i<=n;i++){
        ++num;
        for (j=i;j<=n*n;j+=n)
            f[num][j]=1;
        f[num][n*n+1]=1; 
    }
    for (i=1;i<=n;i++)
        for (j=1;j<=n;j++){
            t=sqrt(a[i][j]);l=a[i][j];
            for (k=2;k<=t;k++){
                if (k*k>l) break;
                if (l%k) continue;
                hash(k);
                while (!(l%k)) l/=k;
            }   
            if (l>1) hash(l);
        }
    for (k=1;k<=b[0];k++){
        ++num;
        for (i=1;i<=n;i++)
            for (j=1;j<=n;j++){
                t=a[i][j];l=0;
                while (!(t%b[k])) t/=b[k],l++;
                f[num][(i-1)*n+j]=l%2;
            }
        f[num][n*n+1]=0;
    }
    while (num<n*n){++num;
        for (j=1;j<=n*n+1;j++)
            f[num][j]=f[1][j];
    }
    for (j=1;j<=n*n;j++){
        for (i=j;i<=num;i++)
            if (f[i][j]){
                for (k=j;k<=n*n+1;k++)
                    swap(f[i][k],f[j][k]);
                break;
            }
        for (i=j+1;i<=num;i++){
            if (!f[i][j]) continue;
            for (k=j;k<=n*n+1;k++)
                f[i][k]=(f[i][k]^f[j][k]);
        }
        if (!f[j][j]){
            t=1e9;
            for (i=j;i<=num;i++){
                k=j;
                while (!f[i][k] && k<=n*n) k++;
                if (k==n*n+1) k=j-1;
                if (k<t) t=k,p=i;
            }
            for (k=j;k<=n*n+1;k++)
                swap(f[p][k],f[j][k]);
        }
    }
    for (j=num;j>=1;j--){
        t=f[j][n*n+1];
        if (!t) continue;
        for (k=1;k<=n*n;k++)
            if (f[j][k]) break;
        if (k==n*n+1){
            printf("0\n");return 0;
        }
    }
    for (j=n*n;j>=1;j--){
        t=f[j][n*n+1];
        for (k=j+1;k<=n*n;k++)
            t=(t^(f[j][k]*c[k]));
        if (t) c[j]=1;
        else if (!f[j][j]) ans++;
    }
    ans=mi(ans);
    printf("%d\n",ans);
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值