• P1072 [NOIP2009 提高组] Hankson 的趣味题


    题目描述

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

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

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

    2. 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×10^{9} 且 n≤2000。

    题解

    没错我不是来宣传正解的,我是来说一种毒瘤解法的 。

    哦哦,如果你不想看讲基本做法。你可以向下翻。

    嗯,其实遇到这种单纯的gcd  or  lcm的题,我们都可以用一种比较简单的方法分析:唯一分解定理。

    嗯,是整数域的唯一分解,不是多项式域的唯一分解。

    那么其实其他的大佬已经解释得很清楚了,关于这种解法,大部分来讲都是先筛出数据范围上限n​即可。但是有个Bug就是对于每个合数k,都最多有一个质因子是大于k​的由于数据范围过大窝萌没法直接筛,而我正是解决了这个问题(虽然有点慢).

    我们思考对于a0​和a1​而言,假设gcd(x,a0​)=a1​

    那么我们会有比较浅显的结论:

    若a0​=i=1∏m​pici​​  ,  x=i=1∏n​pidi​​

    那么

    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那部分也一样。

    但是这个地方需要注意的是,我们需要考虑2^{2}种不同情况:

    1、两个方程的x均非自由,那么如果不同的话就会无解。

    2&3、gcd或者lcm中有一个非自由,我们需要判断这个非自由的解是否是在另一个的自由区间内,不在就是不合法。

    4、都是自由的,那么就做个差留到最后乘法原理。

    代码大概长这样:

    1. inline void Linearity(){
    2. T = qr() ;
    3. Chk[1] = Chk[0] = 1 ;
    4. for (i = 2 ; i <= MAX ; ++ i){
    5. if (!Chk[i]) P[++ P[0]] = i ;
    6. for (j = 1; j <= P[0] && i * P[j] <= MAX ; ++ j){
    7. Chk[i * P[j]] = 1 ;
    8. if (i % P[j] == 0) break ;
    9. }
    10. }
    11. }
    12. inline void work(int ST, int ED){
    13. for(i = ST; i <= ED ; ++ i){
    14. N1 = N2 = N3 = N4 = 0 ;
    15. while (!(A0 % P[i])) A0 /= P[i], ++ N1 ;
    16. while (!(A1 % P[i])) A1 /= P[i], ++ N2 ;
    17. while (!(B0 % P[i])) B0 /= P[i], ++ N3 ;
    18. while (!(B1 % P[i])) B1 /= P[i], ++ N4 ;
    19. if (N1 > N2 && N3 < N4){
    20. if (N2 == N4) A[i] = B[i] = 1 ;
    21. else {mark = 0; break ;}
    22. continue ;
    23. }
    24. if (N1 > N2){
    25. if (N4 >= N2) A[i] = B[i] = N2 ;
    26. else {mark = 0; break ;}
    27. continue ;
    28. }
    29. if (N3 < N4){
    30. if (N4 >= N2) A[i] = B[i] = N3 ;
    31. else {mark = 0; break ;}
    32. continue ;
    33. }
    34. else {
    35. if (N4 >= N2) A[i] = N2, B[i] = N4 ;
    36. else {mark = 0; break ;}
    37. }
    38. }
    39. }

    那么接下来的问题就是该怎么确定最后一个质因子。有一个很显然的做法是由于是最后一个质因子,所以我们只需要判断一下分解完质因数每一个是不是1即可,不是1的话,那就肯定是未筛到的,我们直接让他加入prime数组即可。哦,对,还需要再筛一遍,详情看代码即可。

    1. #include
    2. #include
    3. #include
    4. #define MAX 45000
    5. #define ll long long
    6. using namespace std ;
    7. bitset Chk ; int A0, A1, B0, B1 ;
    8. int Ans, T, i, j, P[MAX >> 2] ; bool mark ;
    9. int N1, N2, N3, N4, A[MAX >> 2], B[MAX >> 2], Txt ;
    10. inline int qr(){
    11. int k = 0 ; char c = getchar() ;
    12. while(!isdigit(c)) c = getchar() ;
    13. while(isdigit(c)) k = (k << 1) + (k << 3) + c - 48, c = getchar() ;
    14. return k ;
    15. }
    16. inline void Linearity(){
    17. T = qr() ;
    18. Chk[1] = Chk[0] = 1 ;
    19. for (i = 2 ; i <= MAX ; ++ i){
    20. if (!Chk[i]) P[++ P[0]] = i ;
    21. for (j = 1; j <= P[0] && i * P[j] <= MAX ; ++ j){
    22. Chk[i * P[j]] = 1 ;
    23. if (i % P[j] == 0) break ;
    24. }
    25. }
    26. }
    27. inline void work(int ST, int ED){
    28. for(i = ST; i <= ED ; ++ i){
    29. N1 = N2 = N3 = N4 = 0 ;
    30. while (!(A0 % P[i])) A0 /= P[i], ++ N1 ;
    31. while (!(A1 % P[i])) A1 /= P[i], ++ N2 ;
    32. while (!(B0 % P[i])) B0 /= P[i], ++ N3 ;
    33. while (!(B1 % P[i])) B1 /= P[i], ++ N4 ;
    34. if (N1 > N2 && N3 < N4){
    35. if (N2 == N4) A[i] = B[i] = 1 ;
    36. else {mark = 0; break ;}
    37. continue ;
    38. }
    39. if (N1 > N2){
    40. if (N4 >= N2) A[i] = B[i] = N2 ;
    41. else {mark = 0; break ;}
    42. continue ;
    43. }
    44. if (N3 < N4){
    45. if (N4 >= N2) A[i] = B[i] = N3 ;
    46. else {mark = 0; break ;}
    47. continue ;
    48. }
    49. else {
    50. if (N4 >= N2) A[i] = N2, B[i] = N4 ;
    51. else {mark = 0; break ;}
    52. }
    53. }
    54. }
    55. int main(){
    56. freopen("son.in", "r", stdin) ;
    57. freopen("son.out", "w", stdout) ;
    58. Linearity() ;
    59. while(T --){
    60. Ans = 1, mark = 1 ;
    61. A0 = qr(), A1 = qr(), B0 = qr(), B1 = qr() ;
    62. work(1, P[0]) ;
    63. if (A0 != 1 || A1 != 1 || B0 != 1 || B1 != 1){
    64. Txt = P[0] + 1 ;
    65. if (B1 != 1) P[++ P[0]] = B1 ;
    66. if (A1 != 1 && A1 != B1) P[++ P[0]] = A1 ;
    67. if (A0 != 1 && A0 != B1 && A0 != A1) P[++ P[0]] = A0 ;
    68. if (B0 != 1 && B0 != B1 && B0 != A1 && B0 != A0) P[++ P[0]] = B0 ;
    69. work(Txt, P[0]) ;
    70. }
    71. for(i = 1; i <= P[0] && mark ; ++ i) Ans *= (B[i] - A[i] + 1) ;
    72. if (!mark) putchar('0'), putchar('\n') ;
    73. else printf("%d\n", Ans) ;
    74. }
    75. }

    最后还有彩蛋哦:

    1、这个题中的关键代码,就是work函数是在作者事先考虑清楚,事中如同做梦,事后不可思议的情况下写出来的……也就是说当时写代码的时候码力突然增强了一个量级

    2、关于什么自由不自由的定义……哈哈哈哈那只是我的突发奇想而已~~不是故意哲学!不是!~~但是你会发现以下的文字阐述确实会简练好多啊

    3、其实你如果去不找另一个比较大的质数,也是可以得90分的!从loj的数据来看,前面的测试点一路顺风,只有最后一个测试点是专门卡这一点的,因为出现了好多行答案不相同的情况

    4、其实我觉得我最后的操作是跟AlphaGo动态学习处理信息有点异曲同工之处的,所以为了这个必须来写题解! 

  • 相关阅读:
    【目标检测】41、CSPNet | 一种加强 CNN 模型学习能力的主干网络
    vue实现无感刷新token
    java毕业生设计校园兼职招聘系统计算机源码+系统+mysql+调试部署+lw
    使用matlab辨识工具来估算震动系统的传递函数
    深度学习在新冠肺炎检测方面的应用
    NetCore开发的分布式文件上传系统
    Qt——常用控件详解
    Altium Designer培训 | 2 - 原理图库创建篇
    C#为什么非要把函数叫方法?
    虹科干货丨TWAMP:什么是双向主动测量协议?
  • 原文地址:https://blog.csdn.net/You_are_hanson/article/details/126060297