2016ccpc 1002(hdu5833)题解 (高斯消元求异或方程组自由变元)

本文介绍了一种使用高斯消元法解决特定线性方程组问题的方法,通过将数字分解为素数的幂次,进而转化为求解线性方程组的自由变元数量,最终得到满足条件的组合数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

比赛结束才知道是个高斯消元的题目,吓得我赶紧学了一发,然后惊讶的发现白皮书上原题QAQ.

由于刚学会,虽然是手敲但有些细节还是比对了模板,所以并不能解释,先放一发代码,等熟练了再补.



-------------------------------------分割线---------------------------------------------


时隔两个月终于有时间和精力来把这题来理一理了。


这个题比较重要的一点是想到把每个数分解成素数的幂次,这样的话, 每个数相乘的结果,只要每个素数的幂都是偶数,就能保证乘积是平方数了。

所以把每个数分解,列出每个数在所有可能的素因子的幂的加和,要求结果为偶数,这样的话等价于每一项都对2取模,每个等式右边要求结果都为0。

如果能分解成n个素因子,那么就有n个等式,每个等式等于号左边是每个数在这个素因子上的幂对2取模,等式右边为0.

现在求有几种组合能使乘积为平方数,即线性方程组有多少个解,那么简单了,只需要求出自由变元数量ans,2的ans次-1即答案,因为题目要求不能一个数都不取,所以需要减去都为0的情况。求自用变元就是高斯消元模板了,在代码里介绍吧。


代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cstdlib>
using namespace std;
int prim[400];
int v[2005];
long long x[400];
int a[400][400];
int k;
const int mod=1000000007;
void pri(int maxn)
{
    int i, j;
    for(i=2; i<=maxn; i++)
    {
        if(!v[i])
        {
            prim[k++]=i;
            for(j=i; j<=maxn; j+=i)
            {
               v[j]=0;                                                                                   v[j]=1;
            }
        }
    }
}
long long quick_mod(int m, long long n)
{
    long long ret=1;
    long long term=m;
    while(n>0)
    {
        if(n%2==1)ret=(ret*term)%mod;
        n>>=1;
        term=(term*term)%mod;
    }
    return ret;
}
int gauss(int var, int equ)
{
    int i, j;
    int col, maxr;
    int k=0;
    for(k=0, col=0; k<equ && col<var; k++, col++)
    {
        maxr=k;
        for(i=k; i<equ; i++)
        {
            if(abs(a[i][col])>abs(a[maxr][col]))maxr=i; //找到这一列最大的那个数,用于消去这一列以下的数,这里为1即可
        }
        if(a[maxr][col]==0)  //如果这一列已经都为0,继续对当前的行操作
        {
            k--;
            continue;
        }
        for(j=col; j<var+1; j++)
        {
            swap(a[k][j], a[maxr][j]);   //将选出的这一列最大的数对应的行交换到k行
        }
        for(i=k+1; i<equ; i++)
        {
           if(a[i][col]!=0)  for(j=col; j<var+1; j++)
            {
                {
                    a[i][j]^=a[k][j];  // 消去col的1
                }
            }
        }
    }
    for(i=k; i<equ; i++)
    {
        if(a[i][col]!=0)return -1;
    }
    if(var>k)return var-k;  //变元数减去秩即自由变元数
    return 0;
}
int main()
{
    int t;
    pri(2000);

    scanf("%d", &t);
    int e=1;
    while(t--)
    {
        int n;
        scanf("%d", &n);
        int i, j;
        memset(a,0,sizeof(a));
        for(i=0; i<n; i++)
        {
            scanf("%lld", &x[i]);
        }
        for(i=0; i<n; i++)
        {
            for(j=0; j<k; j++)
            {
                int c=0;
                while(x[i]%prim[j]==0)
                {
                    x[i]/=prim[j];
                    c++;
                }
                if(c&1)
                a[j][i]=1;
            }
        }
        long long ans=gauss(n, k);
        printf("Case #%d:\n", e++);
        printf("%lld\n", quick_mod(2,ans)-1);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值