【HDU 5833】Zhu and 772002(异或方程组高斯消元讲解)

题目大意:给出n个数字a[],将a[]分解为质因子(保证分解所得的质因子不大于2000),任选一个或多个质因子,使其乘积为完全平方数。求其方法数。

学长学姐们比赛时做的,当时我一脸懵逼的不会搞……所以第二天上午花了一上午学习了一下线性代数。

题目思路:

任选一个或多个质因子,起乘积为完全数m,因为组成它的均为素数,假设组成m的素数的种类为n,那么这n类素数中每类素数的个数应为偶数。

可设:a[i][j]=0代表第i种素数可在a[j]中分离出的个数为偶数,a[i][j]=1代表第i种素数可在a[j]中分离出的个数为奇数数。

          b[i]=1代表选择这类素数,b[i]=0代表不选择这类素数。

列出线性方程组:

a11x1+a12x2+...+a1nxn=0

a21x1+a22x2+...+a2nxn=0

...

an1x1+an2x2+...+annxn=0

求解的个数 ans

转化为矩阵形式:

矩阵A=

a11 a12 a13 …………a1n

a21 a22 a23 …………a2n

……………………………………

……………………………………

an1 an2 an3 …………ann

通过初等变换可将矩阵A换成类似下面矩阵B的形式

a11 a12 a13 ……a1n

0     a22 a23 ……a2n

0       0    a33……a3n

0       0    0    ……arn

再将矩阵B转化成线性方程组 可求出最后一组方程的解,倒着往回求可求出所有方程解

可解出的方程组共 r 个,由秩的定义可知 r等矩阵A的秩

由定理:

对于n元齐次线性方程组如果r<n,则方程组含n-r个自由未知量。

解的个数ans=2^(n-r)-1(减去全0解)

具体操作:

 

#include<cstdio>
#include<stdio.h>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
#define INF 0x3f3f3f
#define MAX 2105
#define MOD 1000000007

using namespace std;

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

void MakeTab()//打素数表
{
    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()
{
    MakeTab();
    int i,j,cns=1,T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%lld",&n);
        memset(c,0,sizeof(c));
        for(i=1; i<=n; i++)
        {
            scanf("%lld",&a[i]);
        }

        for(i=1; i<=n; i++)
        {
            for(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(i=1; i<=k; i++)
            ans=(ans*2)%MOD;
        printf("Case #%d:\n",cns++);
        printf("%lld\n",ans-1);//去掉全0的解
    }
    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/alan-W/p/5773663.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值