1. 程式人生 > >LightOJ-1236- Pairs Forming LCM (算術基本定理)

LightOJ-1236- Pairs Forming LCM (算術基本定理)

原題連結:
Find the result of the following code:

long long pairsFormLCM( int n ) {
long long res = 0;
for( int i = 1; i <= n; i++ )
for( int j = i; j <= n; j++ )
if( lcm(i, j) == n ) res++; // lcm means least common multiple
return res;
}

A straight forward implementation of the code may time out. If you analyze the code, you will find that the code actually counts the number of pairs (i, j) for which lcm(i, j) = n and (i ≤ j).
Input
Input starts with an integer T (≤ 200), denoting the number of test cases.
Each case starts with a line containing an integer n (1 ≤ n ≤ 1014).
Output
For each case, print the case number and the value returned by the function ‘pairsFormLCM(n)’.
Sample Input
15
2
3
4
6
8
10
12
15
18
20
21
24
25
27
29

Sample Output
Case 1: 2
Case 2: 2
Case 3: 3
Case 4: 5
Case 5: 4
Case 6: 5
Case 7: 8
Case 8: 5
Case 9: 8
Case 10: 8
Case 11: 5
Case 12: 11
Case 13: 3
Case 14: 4
Case 15: 2
題意:
找出能夠滿足(a,b)的最小公倍數是n的所有可能,輸出可能數。
題解:
利用算術基本定理,每一個數都可以被分解為素數的指數的乘積:
素因子分解:n = p1 ^ e1 * p2 ^ e2 *…*pn ^ en
另一個定理:
現在取n的兩個因子a,b
a=p1 ^ a1 * p2 ^ a2 *…*pn ^ an
b=p1 ^ b1 * p2 ^ b2 *…*pn ^ bn
gcd(a,b)=p1 ^ min(a1,b1) * p2 ^ min(a2,b2) *…*pn ^ min(an,bn)
lcm(a,b)=p1 ^ max(a1,b1) * p2 ^ max(a2,b2) *…pn ^ max(an,bn)
可以看到,只要max(ax,bx)ex,就能夠保證a,b的最小公倍數是n,所有當ax

ex時,bx有0~ex種取法,同理:當bxex時,ax也有0~ex種取法,去除掉axbx==ex的重複情況,則每一個位置都有2ex+1種取法。每個位置的種類都相乘即可。
附上AC程式碼:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
#define LL long long

const int N=1e7+5;
const int NN=1e6;
int prime[NN],cnt=0;
bool vis[N];
void el()//素數打表
{
    memset(vis,0,sizeof(vis));
    for(int i=2;i<N;i++)
    {
        if(!vis[i])
        {
            prime[cnt++]=i;
            for(int j=i+i;j<N;j+=i)
            {
                vis[j]=1;
            }
        }
    }
}

int main()
{
    el();
    int t;
    LL n,sum;
    scanf("%d",&t);
    for(int cas=1;cas<=t;cas++)
    {
        sum =1;
        scanf("%lld",&n);
        for(int i=0;i<cnt&&prime[i]*prime[i]<=n;i++)
        {
            if(n%prime[i]==0)//可以整除
            {
                int e=0;
                while(n%prime[i]==0)//唯一分解定理
                {
                    n/=prime[i];
                    e++;
                }
                sum*=(2*e+1);//每一個素數的可能取值種類
            }
        }
        if(n>1)
            sum*=(2*1+1);//如果最終不能被分解完,則證明最終剩餘還有一個素數,種類和之前一樣
        printf("Case %d: %d\n",cas,(sum+1)/2);//去除(i,j)、(j、i)的重複情況
    }
    return 0;
}

歡迎評論!