UVa11542Squre——异或方程组&&高斯消元法
题意
给出 $n$ 个整数,从中选出1个或多个,使得选出的整数乘积是完全平方数。一共有多少种选法?($1 \leq n \leq 100$,$1 \leq a_i \leq 10^{15}$ 且不含大于500的素因子)
分析
“不含大于500的素因子”提示我们考虑每个数的素因数分解,用01向量表示一个数,再用 $n$ 个01变量 $x_i$ 来表示我们的选择,其中 $x_i=1$ 表示要选第 $i$ 个数,$x_i=0$ 表示不选第 $i$ 个数,则对每个素数的幂列出一个模2的方程。
如果要让这个数是完全平方数,每个幂都应该是偶数,即模2为0.
在模2的剩余系中每个数都是0/1,加法可换成异或,于是就成了一个异或方程组。
xor方程组很好消元的,因为不需要做乘法和除法,只需要做xor;每次也不需要找绝对值最大的系数(因为系数不是0就是1,只需系数为1即可消元)
设自由变量为 $r$,则最终答案为 $2^r-1$,减1是因为本题不允许一个整数都不选。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 500 + 10; typedef int Matrix[maxn][maxn]; //m个方程,n个变量 int Rank(Matrix A, int m, int n) //求矩阵的秩 { int i = 0, j = 0, k, r, u; while(i < m && j < n) //当前正在处理第i个方程的第j个变量 { r = i; for(k = i;k < m;k++) if(A[k][j]){r =k; break;} if(A[r][j]) { if(r != i) for(k = 0; k <= n;k++) swap(A[r][k], A[i][k]); for(u = i+1; u < m;u++) if(A[u][j]) for(k = i;k <= n;k++) A[u][k] ^= A[i][k]; i++; } j++; } return i; } //返回n以内素数的个数 //埃氏筛法O(nloglogn) const int maxs = 500 + 10; int prime[maxs]; //prime[i]表示第i个素数 bool is_prime[maxs + 1]; //is_prime[i]为true表示i是素数 int sieve(int n) { int cnt = 0; for (int i = 0; i <= n; i++) is_prime[i] = true; is_prime[0] = is_prime[1] = false; for (int i = 2; i <= n; i++) { if (is_prime[i]) { prime[cnt++] = i; for (int j = i * i; j <= n; j += i) is_prime[j] = false; //i * i可能爆int } } return cnt; } Matrix A; int main() { int m = sieve(500); int T; scanf("%d", &T); while(T--) { int n, max_p=0; ll x; scanf("%d", &n); memset(A, 0, sizeof(A)); //记得置零 for(int i = 0;i < n;i++) { scanf("%lld", &x); for(int j = 0;j < m;j++) { while(x % prime[j] == 0) { max_p = max(max_p, j); x /= prime[j]; A[j][i] ^= 1; } } } int r = Rank(A, max_p+1, n); printf("%lld\n", (1LL << (n-r))-1); } return 0; }
From:
《算法竞赛入门经典训练指南》——刘汝佳、陈锋著
个性签名:时间会解决一切