HDU 5833 Zhu and 772002

高斯消元解异或方程。
题意:

给你n个数,然后让你选择一些数,乘起来成为完全平方数,问你有多少种方案。
分析:

例如四个整数4,6,10,15。他们的素因子只有2,3,5这3种,把他们写成01向量的形式4 = 2^2  * 3^0 * 5 ^ 0  ->(2,0,0);
6 = 2^1 * 3^1 * 5 ^0 ->(1,1,0), 10 = 2^1 * 3^0 * 5^1 ->(1,0,1), 15 = 2^0 * 3^1 * 5^1 ->(0,1,1).
选出来的数乘积为2^(2*x1 + x2 + x3) * 3^(x2 + x4) * 5^(x3 + x4); 要使这个数为平方数,每个幂都为偶数,
即             
    x2+x3 = 0(mod2)
    x2+x4 = 0(mod2)
    x3+x4 = 0(mod2)
或   
   x2 xor x3 = 0
   x2 xor x4 = 0
   x3 xor x4 = 0
就变成了一个求线性方程组的秩, 最后减一, 因为题意不允许一个都不选,即0 0 0 0这组不成立。
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define MAX 2000 + 10
#define MOD 1000000007

using namespace std;

long long c[MAX][505], p[MAX], v[MAX], a[MAX], cnt, n;//c矩阵,p素数表,cnt素数的个数

void mprime()//打素数表
{
    int i,j;
    memset(v,0,sizeof(v));
    memset(p,0,sizeof(p));
    cnt=0;
    for(i=2; i<=2000; i++)
    {
        if(!v[i])
        {
            p[++cnt]=i;
            for(j=i; j<=2000; j+=i)
            {
                v[j]=1;
            }
        }
    }
}

int Rank()//计算秩
{
    int i,j,k,r,u;
    i=0;
    j=0;
    while(i<=cnt && j<=n)
    {
        r=i;
        while(!c[r][j] && r<=cnt)
            r++;
        if(c[r][j])
        {
            swap(c[i],c[r]);//如果发现了第r行第j列为1,就讲r行和i行行互换(初等行变换)
            for(u=i+1; u<=cnt; u++)
            {
                if(c[u][j])
                {
                    for(k=i; k<=n; k++)
                    {
                        c[u][k]=c[u][k]^c[i][k];//每找到一个未知数就对其进行亦或处理,去掉系数c[i][k]
                    }
                }
            }
            i++;
        }
        j++;
    }
    return i;
}

int main()
{
    mprime();
    int cns = 1, T;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%lld", &n);
        memset(c, 0, sizeof(c));
        for(int i = 1; i <= n; i++)
        {
            scanf("%lld", &a[i]);
        }

        for(int i = 1; i <= n; i++)
        {
            for(int j = 1; j <= cnt; j++)
            {
                long long num = a[i];
                if(num%p[j] == 0)
                {
                    while(num%p[j] == 0)
                    {
                        num /= p[j];
                        c[j][i] = c[j][i]^1;
                    }
                }
            }
        }

        long long k = (n-Rank());
        long long ans = 1;
        for(int i = 1; i <= k; i++)
            ans = (ans*2) % MOD;
        printf("Case #%d:\n", cns++);
        printf("%lld\n", ans-1);
    }
    return 0;
}



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值