luogu1072 Hankson的趣味题
题目大意
给出数a0, a1, b0, b1,求满足gcd(a0, x)=a1, lcm(b0, x)=b1的x的个数
解法一
枚举b1的因数,看看是否满足上述条件。
怎样枚举因数
试除法。对于1~sqrt(b1)的所有数i,若其能被b1整除,则i和b1/i便是b1的质因数。
注意
- 是sqrt(b1),而不是sqrt(b1)+1。不要擅自主张,因为这样sqrt(b1)~sqrt(b1+1)中的能被b1整除的数就被算重了。
- 求lcm时,应当为a/gcd(a,b)*b,而不是a*b/gcd(a,b)。因为a*b可能会爆int。
#include <cstdio> #include <cstring> #include <cstdarg> #include <cmath> #include <cassert> using namespace std; const int MAX_P = 45000, MAX_P_CNT = 30000; int Primes[MAX_P_CNT]; int A, B, Gcd, Lcm, AnsCnt, N; void GetPrime(int *prime, int n) { static bool NotPrime[MAX_P]; memset(NotPrime, false, sizeof(NotPrime)); int primeCnt = 0; for (int i = 2; i <= n; i++) { if (!NotPrime[i]) prime[primeCnt++] = i; for (int j = 0; j < primeCnt; j++) { if (prime[j] * i > n) break; NotPrime[prime[j] * i] = true; if (i%prime[j] == 0) break; } } } int GetGcd(int a, int b) { return b ? GetGcd(b, a%b) : a; } int GetLcm(int a, int b) { return a / GetGcd(a, b) * b;//易忘点:Not a * b / GetGcd(a, b) } void Dfs(int pos, int prod, int curTime) { if (pos>=0 && Primes[pos] > N) return; assert(Lcm%prod == 0); if (curTime > 0) { if (GetGcd(prod, A) == Gcd && GetLcm(prod, B) == Lcm) AnsCnt++; int d = Lcm / prod; if (d != prod && GetGcd(d, A) == Gcd && GetLcm(d, B) == Lcm) AnsCnt++; } for (int time = 0; prod <= N && Lcm%prod==0; time++) { Dfs(pos + 1, prod, time); prod *= Primes[pos + 1]; } } int main() { GetPrime(Primes, MAX_P); int caseCnt; scanf("%d", &caseCnt); while (caseCnt--) { scanf("%d%d%d%d", &A, &Gcd, &B, &Lcm); AnsCnt = 0; N = sqrt(Lcm);//易忘点:Don't plus 1 Dfs(-1, 1, 1); printf("%d\n", AnsCnt); } return 0; }
解法二
每个数都可以用Πp[i]^m[i](p[i]是质数)表示,对于两个数a,b的最大公因数,它每一个p[i]的m[i]是a,b的m[i]中的较小值,最小公倍数是较大值。最终的结果便是每个p[i]计算出的满足较大较小关系的x的取值范围的大小。
注意
枚举n的质因数时,可能存在一个质数p>sqrt(n)。此时不要忘了它呀!
#include <cstdio> #include <cstring> #include <algorithm> #include <cstdarg> using namespace std; const int MAX_P = 45000, MAX_P_CNT = 3000; int Primes[MAX_P_CNT]; int TotPrime; int GetPrime(int *prime, int n) { static bool NotPrime[MAX_P]; memset(NotPrime, false, sizeof(NotPrime)); int primeCnt = 0; for(int i=2; i<=n; i++) { if (!NotPrime[i]) prime[primeCnt++] = i; for (int j = 0; j < primeCnt; j++) { if (i*prime[j] > n) break; NotPrime[i*prime[j]] = true; if (i%prime[j] == 0) break; } } return primeCnt; } int GetM(int prime, int &x) { int cnt = 0; while (x%prime == 0) { cnt++; x /= prime; } return cnt; } void DoPrime(int prime, int &ans, int &a, int &gcd, int &b, int &lcm) { int mA = GetM(prime, a), mGcd = GetM(prime, gcd), mB = GetM(prime, b), mLcm = GetM(prime, lcm); if (mA == mGcd && mB == mLcm && mGcd <= mLcm) ans *= mLcm - mGcd + 1;//x *= prime ^ (mGcd ~ mLcm) else if (mA == mGcd && mB < mLcm && mGcd <= mLcm) ans *= 1;//x *= prime ^ (mLcm) else if (mA > mGcd && mB == mLcm && mGcd <= mLcm) ans *= 1;//x *= prime ^ (mGcd) else if (mA > mGcd && mB < mLcm && mGcd == mLcm) ans *= 1;//x *= (mGcd == mLcm) else ans *= 0; } int Proceed(int a, int gcd, int b, int lcm) { int ans = 1, end = max(a, lcm); for (int p = 0; Primes[p] <= end && ans && p<TotPrime; p++) DoPrime(Primes[p], ans, a, gcd, b, lcm); if (a > 1) DoPrime(a, ans, a, gcd, b, lcm); else if (lcm > 1 && lcm != a) DoPrime(lcm, ans, a, gcd, b, lcm); return ans; } int main() { TotPrime = GetPrime(Primes, MAX_P); int caseCnt; scanf("%d", &caseCnt); while (caseCnt--) { int a, gcd, b, lcm; scanf("%d%d%d%d", &a, &gcd, &b, &lcm); printf("%d\n", Proceed(a, gcd, b, lcm)); } return 0; }