【NOIP2009】Hankson 的趣味题

时间:2023-03-09 07:33:49
【NOIP2009】Hankson 的趣味题

题目描述

Hanks 博士是 BT (Bio-Tech,生物技术) 领域的知名专家,他的儿子名叫 Hankson。现在,刚刚放学回家的 Hankson 正在思考一个有趣的问题。

今天在课堂上,老师讲解了如何求两个正整数 c1 和 c2 的最大公约数和最小公倍数。现在 Hankson 认为自己已经熟练地掌握了这些知识,他开始思考一个“求公约数”和“求公倍数”之类问题的“逆问题”,这个问题是这样的:已知正整数 a0,a1,b0,b1,设某未知正整数 x 满足:

1. x 和 a0 的最大公约数是 a1;

2. x 和 b0 的最小公倍数是 b1。

Hankson 的“逆问题”就是求出满足条件的正整数 x。但稍加思索之后,他发现这样的x 并不唯一,甚至可能不存在。因此他转而开始考虑如何求解满足条件的 x 的个数。请你帮助他编程求解这个问题。

解题报告:

写着好玩,好像和网上题解有些不一样

这题可以从c和d下手,考虑c和d的质因子,如果d的某个质因子和c的某个质因子的出现次数相同,那么x就可以取任意个(不超过d)该质因子。

如果c的质因子和d的质因子出现的不相同,那么x含有该因子的次数就确定了,可以直接乘起来。

最后我们把不确定的质因子dfs枚举出现次数,然后暴力判断 \(gcd(x,a0)==a1\) 即可

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#define RG register
#define il inline
#define iter iterator
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
const int N=45005;
int a,b,c,d,pri[N],num=0,n=0;bool vis[N];
int li=1;
void getpri(){
for(int i=2;i<N;i++){
if(!vis[i])pri[++num]=i;
for(int j=1;j<=num && pri[j]*i<N;j++){
vis[pri[j]*i]=true;if(i%pri[j]==0)break;
}
}
}
struct node{
int x,t;
}q[N];
int qm(int x,int k){
int sum=1;
while(k){if(k&1)sum*=x;x*=x;k>>=1;}
return sum;
}
bool check(int y,int goal){
int x=c,cnt=0;
while(x%y==0)x/=y,cnt++;
return cnt!=goal;
}
int ans=0;
int gcd(int x,int y){return x%y?gcd(y,x%y):y;}
void dfs(int dep,int cnt,int tot){
if(dep==n+1){
if(gcd(tot,a)==b)ans++;
return ;
}
if(cnt<q[dep].t)dfs(dep,cnt+1,tot*q[dep].x);
dfs(dep+1,0,tot);
}
void work()
{
int cnt=0,x;n=0;ans=0;li=1;
scanf("%d%d%d%d",&a,&b,&c,&d);x=d;
for(int j=1;j<=num && pri[j]<=x;j++){
cnt=0;
while(x%pri[j]==0)x/=pri[j],cnt++;
if(check(pri[j],cnt))li*=qm(pri[j],cnt);
else q[++n].x=pri[j],q[n].t=cnt;
}
if(x>1){
if(check(x,1))li*=x;
else q[++n].x=x,q[n].t=1;
}
dfs(1,0,li);
printf("%d\n",ans);
} int main()
{
int T;cin>>T;getpri();
while(T--)work();
return 0;
}