HDU 5833 高斯消元 异或方程组

题意:给出n个正整数,从中选出1个或者多个,使得选出来的整数乘积是完全平方数,一共有多少种选法。


思路:用01向量表示一个数,再用n个01向量来表示我们的选择,因为完全平方数要求素因子的次数一定要是偶数的,所以我们可以统计的将奇数当作1,偶数当作0,那么就是一组可以变换成oxr的方程组,最后的结果有自由变量f个,答案是2^f-1,f求解就是求n-方程组的秩,(本题不允许一个都不选,所以减去1,即去掉自由元全取0的这种情况)。

求的是方程解的个数,方程解的个数等于  2^自由变量的个数。

详解见白书 161页。

#include<bits/stdc++.h>
#define LL long long
#define INF 0x3f3f3f3f
#define mem(a,b) memset(a,b,sizeof(a))
#define bug puts("***********")
using namespace std;
const int N =510;
const LL mod =1000000007;
struct Mat{
    int mat[N][N];
}sta;
int cnt=0;
int vis[2010];
int prime[N];
void init(){
    mem(vis,0);
    for(int i=2;i<2010;i++){
        if(vis[i]==0){
            prime[cnt++]=i;
            for(int j=i*i;j<2010;j+=i)
                vis[j]=1;
        }
    }
}
int ran(Mat a,int n,int m){
    int i=0,j=0,r;
    while(i<n&&j<m){  ///第i个方程 第j个变量。
        int r=i;
        for(int k=i;k<n;k++){
            if(a.mat[k][j]){
                r=k;
                break;
            }
        }
        if(a.mat[r][j]){
            if(r!=i){
                for(int k=0;k<=m;k++)swap(a.mat[r][k],a.mat[i][k]);
            }
            for(int k=i+1;k<n;k++){
                if(a.mat[k][j]){
                    for(int g=0;g<=m;g++)
                        a.mat[k][g]^=a.mat[i][g];
                }
            }
            i++;
        }
        j++;
    }
    return i;
}
int main(){
    init();///cnt 个方程。
    int t,n;
    int maxn=0;
    scanf("%d",&t);
    LL x;
    int cas=1;
    while(t--){
        mem(sta.mat,0);
        scanf("%d",&n);
        for(int i=0;i<n;i++){   /// n个变量
            scanf("%lld",&x);
            for(int j=0;j<cnt;j++){
                while(x%prime[j]==0){
                    maxn=max(maxn,j);
                    x/=prime[j];
                    sta.mat[j][i]^=1;
                }
            }
        }
        
        int r=ran(sta,maxn+1,n);
        LL num=1;
        for(int i=1;i<=n-r;i++)
            num=num*2%mod;
        printf("Case #%d:\n%lld\n",cas++,(num+mod-1)%mod);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值