POJ 2429 -- GCD & LCM Inverse


http://poj.org/problem?id=2429
Description
Given two positive integers a and b, we can easily calculate the greatest common divisor (GCD) and the least common multiple (LCM) of a and b. But what about the inverse? That is: given GCD and LCM, finding a and b.
Input
The input contains multiple test cases, each of which contains two positive integers, the GCD and the LCM. You can assume that these two numbers are both less than 2^63.
Output
For each test case, output a and b in ascending order. If there are multiple solutions, output the pair with smallest a + b.
Sample Input
3 60
Sample Output
12 15
http://www.hankcs.com/program/cpp/poj-2429-gcd-lcm-inverse.html
分析:由于 G和L分别是a,b的最大公约数和最小公倍数,那么G一定整除L,否则就没有解了。
进一步可以知道L/G的素因子分解式中,同一项不会同时出现在a,b中,因为相同的在G中已经除掉了.
那么有:,所以我们现在只需要枚举L/G的约数,枚举的一个x在a中,那么另一个(L/G)/x就是在b中,但是我们知道
,,所以思路就是对L/G先分解,因为这个数很大,所以就用Pollard-rho分解法吧,分解之后,利用 dfs找因子,然后每找到一个就判断一次来记录a+b最小的,然后就分析完毕。
  1. const int Times=10;  
  2. const LL INF=(LL)1<<61;  
  3. const int N=550;  
  4.   
  5. LL n,m,ct,cnt,mini,mina,minb,ans;  
  6. LL fac[N],num[N];  
  7.   
  8. LL gcd(LL a,LL b)  
  9.  
  10.     return b? gcd(b,a%b):a;  
  11.  
  12.   
  13. LL multi(LL a,LL b,LL m)  
  14.  
  15.      LL ans=0;  
  16.      while(b)  
  17.       
  18.          if(b&1)  
  19.           
  20.              ans=(ans+a)%m;  
  21.              b--;  
  22.           
  23.          b>>=1;  
  24.          a=(a+a)%m;  
  25.       
  26.      return ans;  
  27.  
  28.   
  29. LL quick_mod(LL a,LL b,LL m)  
  30.  
  31.      LL ans=1;  
  32.      a%=m;  
  33.      while(b)  
  34.       
  35.          if(b&1)  
  36.           
  37.              ans=multi(ans,a,m);  
  38.              b--;  
  39.           
  40.          b>>=1;  
  41.          a=multi(a,a,m);  
  42.       
  43.      return ans;  
  44.  
  45.   
  46. bool Miller_Rabin(LL n)  
  47.  
  48.     if(n==2) return true 
  49.     if(n<2||!(n&1)) return false 
  50.     LL a,m=n-1,x,y;  
  51.     int k=0;  
  52.     while((m&1)==0)  
  53.      
  54.         k++;  
  55.         m>>=1;  
  56.      
  57.     for(int i=0;i
  58.      
  59.         a=rand()%(n-1)+1;  
  60.         x=quick_mod(a,m,n);  
  61.         for(int j=0;j
  62.          
  63.             y=multi(x,x,n);  
  64.             if(y==1&&x!=1&&x!=n-1) return false 
  65.             x=y;  
  66.          
  67.         if(y!=1) return false 
  68.      
  69.     return true 
  70.  
  71.   
  72. LL Pollard_rho(LL n,LL c)  
  73.  
  74.      LL x,y,d,i=1,k=2;  
  75.      y=x=rand()%(n-1)+1;  
  76.      while(true 
  77.       
  78.          i++;  
  79.          x=(multi(x,x,n)+c)%n;  
  80.          d=gcd((y-x+n)%n,n);  
  81.          if(1return d;  
  82.          if(y==x) return n;  
  83.          if(i==k)  
  84.           
  85.              y=x;  
  86.              k<<=1;  
  87.           
  88.       
  89.  
  90.   
  91. void find(LL n,int c)  
  92.  
  93.      if(n==1) return 
  94.      if(Miller_Rabin(n))  
  95.       
  96.          fac[ct++]=n;  
  97.          return  
  98.       
  99.      LL p=n;  
  100.      LL k=c;  
  101.      while(p>=n) p=Pollard_rho(p,c--);  
  102.      find(p,k);  
  103.      find(n/p,k);  
  104.  
  105.   
  106. void dfs(LL c,LL value)  
  107.  
  108.      LL s=1,a,b;  
  109.      if(c==cnt)  
  110.       
  111.          a=value;  
  112.          b=ans/a;  
  113.          if(gcd(a,b)==1)  
  114.           
  115.              a*=n;  
  116.              b*=n;  
  117.              if(a+b
  118.               
  119.                  mini=a+b;  
  120.                  mina=a;  
  121.                  minb=b;  
  122.               
  123.           
  124.          return  
  125.       
  126.      for(LL i=0;i<=num[c];i++)  
  127.       
  128.          if(s*value>mini) return 
  129.          dfs(c+1,s*value);  
  130.          s*=fac[c];  
  131.       
  132.  
  133.   
  134. int main()  
  135.  
  136.     while(~scanf("%llu%llu",&n,&m))  
  137.      
  138.          if(n==m)  
  139.           
  140.              printf("%llu %llu\n",n,m);  
  141.              continue 
  142.           
  143.          mini=INF;  
  144.          ct=cnt=0;  
  145.          ans=m/n;  
  146.          find(ans,120);  
  147.          sort(fac,fac+ct);  
  148.          num[0]=1;  
  149.          int k=1;  
  150.          for(LL i=1;i
  151.           
  152.              if(fac[i]==fac[i-1])  
  153.                  ++num[k-1];  
  154.              else  
  155.               
  156.        
http://blog.sina.com.cn/s/blog_c16aeb090101d2ab.html
逆最大公约数和最小公倍数:为了嘲讽 AOJ 0005 GCD and LCM,现在给出两个数的gcd和lcm,求原来的这两个数(限定两数之和最小)。
2.6 数学问题的解题窍门
作者显然高估了读者的数学修养,我对数论一点都不熟悉,这题光靠前面介绍的一点数论皮毛无从下手,还是看了人家的代码才知道要用Rabin-Miller强伪素数测试和Pollard r因数分解算法。这两个算法的详解放在最后面,不时回来复习一下。
基本思路是 
  • (a / gcd) * (b / gcd) = lcm / gcd ,所以需要分解lcm / gcd 。将其分解为互质的两个数,如果这两个数之和最小,那么乘上gcd就是所求的答案。
  • 但是题目数据太大,需要一个高效的素数检测算法,所以采用Rabin-Miller强伪素数测试
  • 然后分解成质因子的n次方之积,从这些n次方中挑选一些作为x,剩下的作为y。枚举x和y的所有可能,找出最小值。
typedef long long LL;
// return (a * b) % m
LL mod_mult(LL a, LL b, LL m) 
{
    LL res = 0;
    LL exp = a % m;
    while (b) 
    {
        if (b & 1) 
        {
            res += exp;
            if (res > m) res -= m;
        }
        exp <<= 1;
        if (exp > m) exp -= m;
        b >>= 1;
    }
    return res;
}
// return (a ^ b) % m
LL mod_exp(LL a, LL b, LL m) {
    LL res = 1;
    LL exp = a % m;
    while (b) 
    {
        if (b & 1) res = mod_mult(res, exp, m);
        exp = mod_mult(expexp, m);
        b >>= 1;
    }
    return res;
}
// Returns:   bool 是否是素数
// Qualifier: Rabin-Miller强伪素数测试
// Parameter: LL n 待检测的数
// Parameter: LL times 
//************************************
bool miller_rabin(LL n, LL times) 
{
    if (n < 2) return false;
    if (n == 2) return true;
    if (!(n & 1)) return false;
    LL q = n - 1;
    int k = 0;
    while (q % 2 == 0) {
        k++;
        q >>= 1;
    }
    // n - 1 = 2^k * q (q是奇素数)
    // n是素数的话,一定满足下面条件
    // (i) a^q ≡ 1 (mod n)
    // (ii) a^q, a^2q,..., a^(k-1)q 中的任何一个对n求模都为-1
    //
    // 所以、当不满足(i)(ii)中的任何一个的时候,就有3/4的概率是合成数
    //
    for (int i = 0; i < times; ++i) 
    {
        LL a = rand() % (n - 1) + 1; // 从1,..,n-1随机挑一个数
        LL x = mod_exp(a, q, n);
        // 检查条件(i)
        if (x == 1) continue;
        // 检查条件(ii)
        bool found = false;
        for (int j = 0; j < k; j++) 
        {
            if (x == n - 1) 
            {
                found = true;
                break;
            }
            x = mod_mult(x, x, n);
        }
        if (found) continue;
        return false;
    }
    return true;
}
LL get_gcd(LL n, LL m) 
{
    if (n < m) swap(n, m);
    while (m) 
    {
        swap(n, m);
        m %= n;
    }
    return n;
}
// Qualifier: Pollard 因数分解算法
LL pollard_rho(LL n, int c) 
{
    LL x = 2;
    LL y = 2;
    LL d = 1;
    while (d == 1) 
    {
        x = mod_mult(x, x, n) + c;
        y = mod_mult(y, y, n) + c;
        y = mod_mult(y, y, n) + c;
        d = get_gcd((x - y >= 0 ? x - y : y - x), n);
    }
    if (d == n) return pollard_rho(n, c + 1);
    return d;
}
#define MAX_PRIME 200000
vector<int> primes;
vector<bool> is_prime;
// 先生成MAX_PRIME内的素数表
void init_primes() 
{
    is_prime = vector<bool>(MAX_PRIME + 1, true);
    is_prime[0] = is_prime[1] = false;
    for (int i = 2; i <= MAX_PRIME; ++i) 
    {
        if (is_prime[i]) 
        {
            primes.push_back(i);
            for (int j = i * 2; j <= MAX_PRIME; j += i) 
            {
                is_prime[j] = false;
            }
        }
    }
}
// 判断是否是素数,优先查表,如果n很大用Rabin-Miller强伪素数测试
bool isPrime(LL n) 
{
    if (n <= MAX_PRIME) return is_prime[n];
    else return miller_rabin(n, 20);
}
// 分解成素因子,小数用素数表,大数用Pollard 因数分解算法
void factorize(LL n, map<LL, int>& factors) 
{
    if (isPrime(n)) 
    {
        factors[n]++;
    }
    else 
    {
        for (int i = 0; i < primes.size(); ++i) 
        {
            int p = primes[i];
            while (n % p == 0) 
            {
                factors[p]++;
                n /= p;
            }
        }
        if (n != 1) 
        {
            if (isPrime(n)) 
            {
                factors[n]++;
            }
            else 
            {
                LL d = pollard_rho(n, 1);
                factorize(d, factors);
                factorize(n / d, factors);
            }
        }
    }
}
pair<LL, LL> solve(LL a, LL b) 
{
    LL c = b / a;
    map<LL, int> factors;
    factorize(c, factors);
    vector<LL> mult_factors; // 每个质因子的n次方,比如2^2和5^1
    for (map<LL, int>::iterator it = factors.begin(); it != factors.end(); it++) 
    {
        LL mul = 1;
        while (it->second) 
        {
            mul *= it->first;
            it->second--;
        }
        mult_factors.push_back(mul);
    }
    LL best_sum = 1e18, best_x = 1, best_y = c;
    // 这是一个取数的过程,一共 2^size 种情况
    for (int mask = 0; mask < (1 << mult_factors.size()); ++mask)
    {
        LL x = 1;
        for (int i = 0; i < mult_factors.size(); ++i)
        {
            if (mask & (1 << i)) x *= mult_factors[i];
        }
        LL y = c / x;
        if (x < y && x + y < best_sum) 
        {
            best_sum = x + y;
            best_x = x;
            best_y = y;
        }
    }
    return make_pair(best_x * a, best_y * a);
}
int main(int argc, char *argv[])
{
    cin.tie(0);
    ios::sync_with_stdio(false);
    init_primes();
    LL a, b;
    while (cin >> a >> b) 
    {
        pair<LL, LL> ans = solve(a, b);
        cout << ans.first << " " << ans.second << endl;
    }
    return 0;
}

关于两个算法的讲解,有一个超棒的文档
Read full article from 2429 -- GCD & LCM Inverse

Labels

LeetCode (1432) GeeksforGeeks (1122) LeetCode - Review (1067) Review (882) Algorithm (668) to-do (609) Classic Algorithm (270) Google Interview (237) Classic Interview (222) Dynamic Programming (220) DP (186) Bit Algorithms (145) POJ (141) Math (137) Tree (132) LeetCode - Phone (129) EPI (122) Cracking Coding Interview (119) DFS (115) Difficult Algorithm (115) Lintcode (115) Different Solutions (110) Smart Algorithm (104) Binary Search (96) BFS (91) HackerRank (90) Binary Tree (86) Hard (79) Two Pointers (78) Stack (76) Company-Facebook (75) BST (72) Graph Algorithm (72) Time Complexity (69) Greedy Algorithm (68) Interval (63) Company - Google (62) Geometry Algorithm (61) Interview Corner (61) LeetCode - Extended (61) Union-Find (60) Trie (58) Advanced Data Structure (56) List (56) Priority Queue (53) Codility (52) ComProGuide (50) LeetCode Hard (50) Matrix (50) Bisection (48) Segment Tree (48) Sliding Window (48) USACO (46) Space Optimization (45) Company-Airbnb (41) Greedy (41) Mathematical Algorithm (41) Tree - Post-Order (41) ACM-ICPC (40) Algorithm Interview (40) Data Structure Design (40) Graph (40) Backtracking (39) Data Structure (39) Jobdu (39) Random (39) Codeforces (38) Knapsack (38) LeetCode - DP (38) Recursive Algorithm (38) String Algorithm (38) TopCoder (38) Sort (37) Introduction to Algorithms (36) Pre-Sort (36) Beauty of Programming (35) Must Known (34) Binary Search Tree (33) Follow Up (33) prismoskills (33) Palindrome (32) Permutation (31) Array (30) Google Code Jam (30) HDU (30) Array O(N) (29) Logic Thinking (29) Monotonic Stack (29) Puzzles (29) Code - Detail (27) Company-Zenefits (27) Microsoft 100 - July (27) Queue (27) Binary Indexed Trees (26) TreeMap (26) to-do-must (26) 1point3acres (25) GeeksQuiz (25) Merge Sort (25) Reverse Thinking (25) hihocoder (25) Company - LinkedIn (24) Hash (24) High Frequency (24) Summary (24) Divide and Conquer (23) Proof (23) Game Theory (22) Topological Sort (22) Lintcode - Review (21) Tree - Modification (21) Algorithm Game (20) CareerCup (20) Company - Twitter (20) DFS + Review (20) DP - Relation (20) Brain Teaser (19) DP - Tree (19) Left and Right Array (19) O(N) (19) Sweep Line (19) UVA (19) DP - Bit Masking (18) LeetCode - Thinking (18) KMP (17) LeetCode - TODO (17) Probabilities (17) Simulation (17) String Search (17) Codercareer (16) Company-Uber (16) Iterator (16) Number (16) O(1) Space (16) Shortest Path (16) itint5 (16) DFS+Cache (15) Dijkstra (15) Euclidean GCD (15) Heap (15) LeetCode - Hard (15) Majority (15) Number Theory (15) Rolling Hash (15) Tree Traversal (15) Brute Force (14) Bucket Sort (14) DP - Knapsack (14) DP - Probability (14) Difficult (14) Fast Power Algorithm (14) Pattern (14) Prefix Sum (14) TreeSet (14) Algorithm Videos (13) Amazon Interview (13) Basic Algorithm (13) Codechef (13) Combination (13) Computational Geometry (13) DP - Digit (13) LCA (13) LeetCode - DFS (13) Linked List (13) Long Increasing Sequence(LIS) (13) Math-Divisible (13) Reservoir Sampling (13) mitbbs (13) Algorithm - How To (12) Company - Microsoft (12) DP - Interval (12) DP - Multiple Relation (12) DP - Relation Optimization (12) LeetCode - Classic (12) Level Order Traversal (12) Prime (12) Pruning (12) Reconstruct Tree (12) Thinking (12) X Sum (12) AOJ (11) Bit Mask (11) Company-Snapchat (11) DP - Space Optimization (11) Dequeue (11) Graph DFS (11) MinMax (11) Miscs (11) Princeton (11) Quick Sort (11) Stack - Tree (11) 尺取法 (11) 挑战程序设计竞赛 (11) Coin Change (10) DFS+Backtracking (10) Facebook Hacker Cup (10) Fast Slow Pointers (10) HackerRank Easy (10) Interval Tree (10) Limited Range (10) Matrix - Traverse (10) Monotone Queue (10) SPOJ (10) Starting Point (10) States (10) Stock (10) Theory (10) Tutorialhorizon (10) Kadane - Extended (9) Mathblog (9) Max-Min Flow (9) Maze (9) Median (9) O(32N) (9) Quick Select (9) Stack Overflow (9) System Design (9) Tree - Conversion (9) Use XOR (9) Book Notes (8) Company-Amazon (8) DFS+BFS (8) DP - States (8) Expression (8) Longest Common Subsequence(LCS) (8) One Pass (8) Quadtrees (8) Traversal Once (8) Trie - Suffix (8) 穷竭搜索 (8) Algorithm Problem List (7) All Sub (7) Catalan Number (7) Cycle (7) DP - Cases (7) Facebook Interview (7) Fibonacci Numbers (7) Flood fill (7) Game Nim (7) Graph BFS (7) HackerRank Difficult (7) Hackerearth (7) Inversion (7) Kadane’s Algorithm (7) Manacher (7) Morris Traversal (7) Multiple Data Structures (7) Normalized Key (7) O(XN) (7) Radix Sort (7) Recursion (7) Sampling (7) Suffix Array (7) Tech-Queries (7) Tree - Serialization (7) Tree DP (7) Trie - Bit (7) 蓝桥杯 (7) Algorithm - Brain Teaser (6) BFS - Priority Queue (6) BFS - Unusual (6) Classic Data Structure Impl (6) DP - 2D (6) DP - Monotone Queue (6) DP - Unusual (6) DP-Space Optimization (6) Dutch Flag (6) How To (6) Interviewstreet (6) Knapsack - MultiplePack (6) Local MinMax (6) MST (6) Minimum Spanning Tree (6) Number - Reach (6) Parentheses (6) Pre-Sum (6) Probability (6) Programming Pearls (6) Rabin-Karp (6) Reverse (6) Scan from right (6) Schedule (6) Stream (6) Subset Sum (6) TSP (6) Xpost (6) n00tc0d3r (6) reddit (6) AI (5) Abbreviation (5) Anagram (5) Art Of Programming-July (5) Assumption (5) Bellman Ford (5) Big Data (5) Code - Solid (5) Code Kata (5) Codility-lessons (5) Coding (5) Company - WMware (5) Convex Hull (5) Crazyforcode (5) DFS - Multiple (5) DFS+DP (5) DP - Multi-Dimension (5) DP-Multiple Relation (5) Eulerian Cycle (5) Graph - Unusual (5) Graph Cycle (5) Hash Strategy (5) Immutability (5) Java (5) LogN (5) Manhattan Distance (5) Matrix Chain Multiplication (5) N Queens (5) Pre-Sort: Index (5) Quick Partition (5) Quora (5) Randomized Algorithms (5) Resources (5) Robot (5) SPFA(Shortest Path Faster Algorithm) (5) Shuffle (5) Sieve of Eratosthenes (5) Strongly Connected Components (5) Subarray Sum (5) Sudoku (5) Suffix Tree (5) Swap (5) Threaded (5) Tree - Creation (5) Warshall Floyd (5) Word Search (5) jiuzhang (5)

Popular Posts