http://acm.hdu.edu.cn/showproblem.php?pid=5833
问题概述:输入n个数,可以从当中选出一些数,使得这些数乘积为一个完全平方数,求出总共有多少种不同选法
(不能不选)(http://acm.hdu.edu.cn/showproblem.php?pid=5833)
输入样例: 对应输出:
2 Case #1:
3 3
3 3 4 Case #2:
3 3
2 2 2
高斯消元:
①对于n行m列增广矩阵,初始化p==q==1
②遍历第p行第q列及下面的所有元素,找到一个非0元,如果找到执行步骤③,没有找到q++,继续执行步骤②,直
到q超过m,执行步骤④
③将这一行和第p行交换,交换后再遍历p行q列下面的每一个元素,如果当前元值不为0,则通过行变换将这行所有
元素减去p行元素的一定比例,使得当前元为0,遍历完毕,p++,q++,继续执行步骤②
④矩阵已经化为阶梯形矩阵,矩阵的秩就是p-1, 方程组不同解的个数就是2^(n-p+1)
此题题解:
定理:每个平方数的质因子都有偶数个,而若干个数相乘就是将它们的质因数加在一起
设一个矩阵,矩阵中的元素a[i][j]==0表示第i个元素可以分解出偶数个第j个质因数,a[i][j]==1表示第i个元素可以分解
出奇数个第j个质因数,而这题可以将这个矩阵看做成一个方程组的系数矩阵,且这个方程组每一个方程都等于0,
又因为这道题不允许一个数不选,所以矩阵的全0解非法!最后的答案便是方程组解的个数-1
附录:线性方程组解的判断:
设矩阵A为系数矩阵,列向量b为方程对应的值,矩阵B=(A,b)(增广矩阵),n为未知数的个数(列数)
则有:①R(A)<R(B) ------ 无解
②R(A)==R(B)==n ------ 有一个解
③R(A)==R(B)<n ------ 有多个解
PS:当b为0向量时,方程组为线性齐次方程组,不可能无解(像这道题就是个01线性齐次方程组)
#include<stdio.h> #include<string.h> #include<algorithm> using namespace std; #define mod 1000000007 int pri[2100], a[2100] = {1,1}; int solve(int A[][305], int n, int m); int main(void) { int T, i, j, k, n, mxj, cas, ans, ret, A[305][305]; long long x; k = 0; for(i=2;i<=2000;i++) { if(a[i]==1) continue; pri[++k] = i; for(j=i*i;j<=2000;j+=i) a[j] = 1; } scanf("%d", &T); cas = 1; while(T--) { mxj = 0; scanf("%d", &n); memset(A, 0, sizeof(A)); for(i=1;i<=n;i++) { scanf("%lld", &x); for(j=1;j<=k;j++) { while(x%pri[j]==0) { mxj = max(mxj, j); x /= pri[j]; A[j][i] ^= 1; } } } ret = solve(A, mxj, n); ans = 1; for(i=1;i<=n-ret;i++) ans = (ans*2)%mod; printf("Case #%d:\n%d\n", cas++, ans-1); } return 0; } int solve(int A[][305], int n, int m) { int i, j, p, r, q; p = q = 1; while(p<=n && q<=m) { r = -1; for(i=p;i<=n;i++) { if(A[i][q]) r = i; } if(r==-1) { q++; continue; } for(i=1;i<=m;i++) swap(A[r][i], A[p][i]); for(i=p+1;i<=n;i++) { if(A[i][q]) { for(j=q;j<=m;j++) A[i][j] ^= A[p][j]; } } p++, q++; } return p-1; }
