题目描述
Hanks 博士是 BT(Bio-Tech,生物技术) 领域的知名专家,他的儿子名叫 Hankson。现在,刚刚放学回家的 Hankson 正在思考一个有趣的问题。
今天在课堂上,老师讲解了如何求两个正整数 c1 和 c2 的最大公约数和最小公倍数。现在 Hankson 认为自己已经熟练地掌握了这些知识,他开始思考一个“求公约数”和“求公倍数”之类问题的“逆问题”,这个问题是这样的:已知正整数a0,a1,b0,b1,设某未知正整数 xx 满足:
-
x 和 a0 的最大公约数是 a1;
-
x 和 b0 的最小公倍数是 b1。
Hankson 的“逆问题”就是求出满足条件的正整数 xx。但稍加思索之后,他发现这样的 x 并不唯一,甚至可能不存在。因此他转而开始考虑如何求解满足条件的 x 的个数。请你帮助他编程求解这个问题。
输入格式
第一行为一个正整数 n,表示有 n 组输入数据。接下来的n 行每行一组输入数据,为四个正整数 a0,a1,b0,b1,每两个整数之间用一个空格隔开。输入数据保证 a0 能被 a1 整除,b1 能被 b_0b0 整除。
输出格式
共 n 行。每组输入数据的输出结果占一行,为一个整数。
对于每组数据:若不存在这样的 x,请输出 0,若存在这样的 x,请输出满足条件的 x 的个数;
输入输出样例
输入 #1
2 41 1 96 288 95 1 37 1776
输出 #1
6 2
说明/提示
【样例解释】
第一组输入数据,x 可以是 9,18,36,72,144,288,共有 6 个。
第二组输入数据,x 可以是 48,1776,共有 2 个。
【数据范围】
- 对于 50\%50% 的数据,保证有 1≤a0,a1,b0,b1≤10000 且 n≤100。
- 对于 100\%100% 的数据,保证有 1≤a0,a1,b0,b1≤2× 且 n≤2000。
题解
没错我不是来宣传正解的,我是来说一种毒瘤解法的 。
哦哦,如果你不想看讲基本做法。你可以向下翻。
嗯,其实遇到这种单纯的gcd or lcm的题,我们都可以用一种比较简单的方法分析:唯一分解定理。
嗯,是整数域的唯一分解,不是多项式域的唯一分解。
那么其实其他的大佬已经解释得很清楚了,关于这种解法,大部分来讲都是先筛出数据范围上限n即可。但是有个Bug就是对于每个合数k,都最多有一个质因子是大于k的由于数据范围过大窝萌没法直接筛,而我正是解决了这个问题(虽然有点慢).
我们思考对于a0和a1而言,假设gcd(x,a0)=a1
那么我们会有比较浅显的结论:
若a0=i=1∏mpici , x=i=1∏npidi
那么
a1=i=1∏max(m,n)pimin(ci,di)
那我们反着考虑,对于他们的 gcd——a1 里的 pi 来讲,要么是a0中的,要么是x中的。换句话说,如果ci=min(ci,di),那么di只需要≥ ci即可,也就是说di可以在区间 [ci,∞) 上随便取,我们现在称这个x为自由未知数(free known−number),称这个区间为自由区间(free ranges)。
而如果不一样,就只可能是 ci>min(di,ci),此时没有任何取法,只有可能是 di=min(di,ci),所以就只能有一种选法,我们现在称这个x为非自由未知数(unfree uknown−number)。
同理,lcm那部分也一样。
但是这个地方需要注意的是,我们需要考虑种不同情况:
1、两个方程的x均非自由,那么如果不同的话就会无解。
2&3、gcd或者lcm中有一个非自由,我们需要判断这个非自由的解是否是在另一个的自由区间内,不在就是不合法。
4、都是自由的,那么就做个差留到最后乘法原理。
代码大概长这样:
inline void Linearity(){T = qr() ;Chk[1] = Chk[0] = 1 ;for (i = 2 ; i <= MAX ; ++ i){if (!Chk[i]) P[++ P[0]] = i ;for (j = 1; j <= P[0] && i * P[j] <= MAX ; ++ j){Chk[i * P[j]] = 1 ;if (i % P[j] == 0) break ;}}
}
inline void work(int ST, int ED){for(i = ST; i <= ED ; ++ i){N1 = N2 = N3 = N4 = 0 ;while (!(A0 % P[i])) A0 /= P[i], ++ N1 ;while (!(A1 % P[i])) A1 /= P[i], ++ N2 ;while (!(B0 % P[i])) B0 /= P[i], ++ N3 ;while (!(B1 % P[i])) B1 /= P[i], ++ N4 ;if (N1 > N2 && N3 < N4){if (N2 == N4) A[i] = B[i] = 1 ;else {mark = 0; break ;}continue ;}if (N1 > N2){if (N4 >= N2) A[i] = B[i] = N2 ;else {mark = 0; break ;}continue ;}if (N3 < N4){if (N4 >= N2) A[i] = B[i] = N3 ;else {mark = 0; break ;}continue ;}else {if (N4 >= N2) A[i] = N2, B[i] = N4 ;else {mark = 0; break ;}}}
}
那么接下来的问题就是该怎么确定最后一个质因子。有一个很显然的做法是由于是最后一个质因子,所以我们只需要判断一下分解完质因数每一个是不是1即可,不是1的话,那就肯定是未筛到的,我们直接让他加入prime数组即可。哦,对,还需要再筛一遍,详情看代码即可。
#include <cstdio>
#include <bitset>
#include <iostream>
#define MAX 45000
#define ll long longusing namespace std ;
bitset <MAX> Chk ; int A0, A1, B0, B1 ;
int Ans, T, i, j, P[MAX >> 2] ; bool mark ;
int N1, N2, N3, N4, A[MAX >> 2], B[MAX >> 2], Txt ;inline int qr(){int k = 0 ; char c = getchar() ;while(!isdigit(c)) c = getchar() ;while(isdigit(c)) k = (k << 1) + (k << 3) + c - 48, c = getchar() ;return k ;
}
inline void Linearity(){T = qr() ;Chk[1] = Chk[0] = 1 ;for (i = 2 ; i <= MAX ; ++ i){if (!Chk[i]) P[++ P[0]] = i ;for (j = 1; j <= P[0] && i * P[j] <= MAX ; ++ j){Chk[i * P[j]] = 1 ;if (i % P[j] == 0) break ;}}
}
inline void work(int ST, int ED){for(i = ST; i <= ED ; ++ i){N1 = N2 = N3 = N4 = 0 ;while (!(A0 % P[i])) A0 /= P[i], ++ N1 ;while (!(A1 % P[i])) A1 /= P[i], ++ N2 ;while (!(B0 % P[i])) B0 /= P[i], ++ N3 ;while (!(B1 % P[i])) B1 /= P[i], ++ N4 ;if (N1 > N2 && N3 < N4){if (N2 == N4) A[i] = B[i] = 1 ;else {mark = 0; break ;}continue ;}if (N1 > N2){if (N4 >= N2) A[i] = B[i] = N2 ;else {mark = 0; break ;}continue ;}if (N3 < N4){if (N4 >= N2) A[i] = B[i] = N3 ;else {mark = 0; break ;}continue ;}else {if (N4 >= N2) A[i] = N2, B[i] = N4 ;else {mark = 0; break ;}}}
}
int main(){freopen("son.in", "r", stdin) ;freopen("son.out", "w", stdout) ;Linearity() ;while(T --){Ans = 1, mark = 1 ;A0 = qr(), A1 = qr(), B0 = qr(), B1 = qr() ;work(1, P[0]) ;if (A0 != 1 || A1 != 1 || B0 != 1 || B1 != 1){Txt = P[0] + 1 ;if (B1 != 1) P[++ P[0]] = B1 ;if (A1 != 1 && A1 != B1) P[++ P[0]] = A1 ;if (A0 != 1 && A0 != B1 && A0 != A1) P[++ P[0]] = A0 ;if (B0 != 1 && B0 != B1 && B0 != A1 && B0 != A0) P[++ P[0]] = B0 ;work(Txt, P[0]) ;}for(i = 1; i <= P[0] && mark ; ++ i) Ans *= (B[i] - A[i] + 1) ;if (!mark) putchar('0'), putchar('\n') ;else printf("%d\n", Ans) ;}
}
最后还有彩蛋哦:
1、这个题中的关键代码,就是work函数是在作者事先考虑清楚,事中如同做梦,事后不可思议的情况下写出来的……也就是说当时写代码的时候码力突然增强了一个量级
2、关于什么自由不自由的定义……哈哈哈哈那只是我的突发奇想而已~~不是故意哲学!不是!~~但是你会发现以下的文字阐述确实会简练好多啊
3、其实你如果去不找另一个比较大的质数,也是可以得90分的!从loj的数据来看,前面的测试点一路顺风,只有最后一个测试点是专门卡这一点的,因为出现了好多行答案不相同的情况
4、其实我觉得我最后的操作是跟AlphaGo动态学习处理信息有点异曲同工之处的,所以为了这个必须来写题解!