Pairs Forming LCM LightOJ - 1236(整数唯一分解定理+筛选素数)

Pairs Forming LCM

LightOJ - 1236

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

题意:给你一个n,让你找到n以内的LCM(i,j) = n的个数,其中i <= j


思路:这道题的精髓是运用了整数的唯一分解定理,求约数,倍数,质因数,gcd,lcm,都应该想到这个定理。

下面解释为什么:
我们假设( a , b ) = n ,那么:
我们把每个数都分解开看一下有什么规律
n = p1^a1 * p2^a2 * p3^a3 * …… * pk^ak;
a = p1^e1 * p2^e2 * p3^e3 * …… * pk^ek;
b = p1^d1 * p2^d2 * p3^d3 * …… * pk^dk;
我们发现首先如果要是a b两个数的最小公倍数等于n,对于每个素因子(p1p2…pk)
两个数ab中必须至少要有一个数是完整包含n中的对应的那个素因子的幂所得到的数的。
具体来说比如我们看p1这个素因子,ab中至少有个数包含p1^a1,也就是说e1 = a1或者d1 = a1或者两个都等于,也就是max(e1,d1) = a1.
那么推广到一般情况对于每个素因子 都存在 max(ei,di) = ai;

大家想一下这是为什么?
因为不是要求最小公倍数嘛,最终求法就是 a * b /gcd(a,b)
我们分解看 p1^e1 * …… pk^ek * p1^d1 * …… * pk^dk/ gcd(a,b)
所以我们发现分子中每个素因子会出现两次,一个属于a的一个属于b的,那么最大公因数gcd(a,b)不就是每个素因子的那个较小次幂的的乘积嘛,这样除去这些后,也就是分子中除去了这些幂次较小的后剩下幂次较大的素因子,如果想要最终结果等于n那么这些素因子的幂次必须一一对应n中每个素因子的幂次,这就是为什么ab中至少一个数字必须完整拥有对应素因子。

明白了上面的道理,就为我们找种类数提供的基础
我们每个素因子每个素因子的看
对于第i个素因子pi,ab两个数有多少种情况呢,肯定一个数是全部包含的,但是两个都有可能所以就是2,对于另一个数这个对应素因子的幂次的取值范围应该在[0,ai]
所以另一个数有ai+1种可能,对于这个素因子,看上去总的种类就应该是2*(ai+1)
但是别高兴太早,有一种情况就是两者都是完整包含,这时会多算一次,所以实际的对于每个素因子pi,  a,b两个数的取值可能就是2*(ai+1)-1 = 2*ai+1
那么总的可能就是所有乘起来:
(2*a1+1) * (2*a2+1) * (2*a3+1) * …… * (2*ak+1)
但是这个结果是无顺序情况下的,也就是(a,b)a<b和a>b算作两种
而题目要求a<=b,所以我们只取前一半
就直接除以2。      就可以了吗??
当然不行,别忘了其中的情况并不是所有都是2中,当两个数的素因子都完全包含n中的素因子时,是只有一种的所以除以2后需要再+1,把除没的那个一种情况再加回来
所以实际答案公式是
(2*a1+1) * (2*a2+1) * (2*a3+1) * …… * (2*ak+1) / 2 + 1

注意题目仍然有很多坑,直接判断求每个素因子的次数会超时,需要先筛选除素数存下来,n<=1e14,所以我们需要筛选1e7的素数,但是存素数的数组需要用1e6否则内存超限

code:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
const int maxn = 1e7+10;
const int N = 1e6;
bool isprime[maxn];
int prime[N];
int top;
void init(){
    memset(isprime,true,sizeof(isprime));
    isprime[0] = isprime[1] = false;
    top = 0;
    for(int i = 2; i < maxn; i++){
        if(isprime[i]){
            prime[top++] = i;
            for(int j = i+i; j < maxn; j += i){
                isprime[j] = false;
            }
        }
    }
}
int main(){
    init();
    int t,cas = 0;
    scanf("%d",&t);
    while(t--){
        ll n,ans = 1LL;
        scanf("%lld",&n);
        for(int i = 0; (ll)prime[i] * (ll)prime[i] <= n && i < top; i++){
            if(n % prime[i] == 0){
                int cnt = 0;
                while(n % prime[i] == 0){
                    cnt++;
                    n /= prime[i];
                }
                ans = ans * (ll)(2 * cnt + 1);
            }
            if(n == 1) break;
        }
        if(n != 1) ans = ans * 3;
        printf("Case %d: %d\n",++cas,ans/2+1);
    }
    return 0;
}




n=pk11pk22pkss,a=pd11pd22pdss, b=pe11pe22pess,max(ei,di)=ki,kieidiki[0,ki]2(ki+1)ei=di=kii2(ki+1)1=2ki+1(a,b)(2k1+1)(2k2+1)(2ks+1)(a,b):[(2k1+1)(2k2+1)(2ks+1)+1]/2(n,n)+1
n=pk11pk22pkss,a=pd11pd22pdss, b=pe11pe22pess,max(ei,di)=ki,kieidiki[0,ki]2(ki+1)ei=di=kii2(ki+1)1=2ki+1(a,b)(2k1+1)(2k2+1)(2ks+1)(a,b):[(2k1+1)(2k2+1)(2ks+1)+1]/2(n,n)+1
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值