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

版权声明:欢迎评论与转载,转载时请注明出处! https://blog.csdn.net/wjl_zyl_1314/article/details/84703231

原题链接:
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,所有当axex时,bx有0~ex种取法,同理:当bxex时,ax也有0~ex种取法,去除掉axbx==ex的重复情况,则每一个位置都有2
ex+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;
}

欢迎评论!

猜你喜欢

转载自blog.csdn.net/wjl_zyl_1314/article/details/84703231
今日推荐