题意
t组(2000) 每组abcd 问你有多少个x满足gcd(a,x) == c && lcm(b,x) == d
思路
题面给了0.3s
根据题意可知x一定是d的因子,考虑枚举d的因子在check是否满足上面两个式子(check复杂度logn)
纯暴力枚举d的因子是sqrt(n)的,那么总复杂度 2000 * sqrt(1e9) * logn ≈ 1e8
怎么减小寻找d的因子的复杂度呢?
因为1e9内的一个数的约数<1600个,所以我们可以把sqrt(1e9)内的质数先筛出来,记录d的质因子及每个质因子的次数,然后通过这些质因子和次数dfs爆搜出d的每一个约数,再来check。只有1600个约数爆搜是可做的。此时复杂度2000 * 质因子数量(质数密度:n以内的质数有 n / lnn个 5e4以内最多5000个)
代码
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int MaxN = 5e4 + 5;
typedef long long LL;
int t,a,b,c,d;
int prime[MaxN],pNum = 0;
bool p[MaxN] = {0};
int fcnt;
struct NODE{
int P,s;
}A[1666];//每个质因子及次数
int dcnt,divi[MaxN];//约数
void init(int N){
for(int i = 2;i <= N; i++){
if(p[i] == false) prime[++pNum] = i;
for(int j = 1;j <= pNum && i * prime[j] <= N; j++){
p[i * prime[j]] = true;
if(i % prime[j] == 0) break;
}
}
}
void dfs(int cnt,int cur){//第cnt个质数 当前数字cur
if(cnt == fcnt + 1){
divi[++dcnt] = cur;
return ;
}
int num = A[cnt].s;
for(int i = 0;i <= num; i++){
dfs(cnt + 1,cur);
cur *= A[cnt].P;
}
}
int main()
{
init(MaxN - 5);
scanf("%d",&t);
while(t--){
scanf("%d %d %d %d",&a,&b,&c,&d);
fcnt = 0;
dcnt = 0;
memset(A,0,sizeof A);
memset(divi,0,sizeof divi);
int D = d;
for(int i = 1;prime[i] <= D && i <= pNum; i++){//枚举质因子
int curP = prime[i];
if(D % curP == 0){
int s = 0;
while(D % curP == 0) D /= curP,s++;
A[++fcnt] = {curP,s};
}
}
if(D > 1) A[++fcnt] = {D,1};
dfs(1,1);
int ans = 0;
for(int i = 1;i <= dcnt; i++){
int x = divi[i];
if(__gcd(a,x) == b && (LL)c * x / __gcd(c,x) == d) ans++;
}
printf("%d\n",ans);
}
return 0;
}