【唯一分解定理 && 状态压缩 && 组合数学】LightOJ - 1236 Pairs Forming LCM

Problem Description

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;
}
输入n让你实现这个程序,求出有多少对lcm(i,j) == n。

思路:

参考了一个很好的博客
核心点:N可以唯一分解成有限个质数的乘积N=P1^a1*P2^a2*P3^a3……Pn^an
令a = p1^e1 * p2^e2
令b = p1^e3 * p2^e4
gcd(a, b) = p1^min(e1, e3) * p2^min(e2, e4)
lcm(a, b) = a*b/gcd(a,b) = p1^max(e1, e3) * p2^max(e2, e4)
参考别人的思路:比如24=2^3*3^1:
(1)如果一个数完整地包含了3^1但是没有完整地包含2^3(一个数x完整地包含某个质因数p及其出现的次数t,指x可以被p^t整除),比如3,6,12,那么另一个数必须完整地包含2^3,比如8,24。那么此时有六种组合(3,8),(3,24),(6,8),(6,24),(12,8),(12,24)
(2)若一个数完整地包含2^3但是没有完整地包含3^1,比如8,那么另一个数必须完整地包含3^1,比如3,6,12,24,此时有4个。
(3)若一个数完整地包含了2^3和3^1,比如24,那么另一个数有(3+1)*(1+1)种可能,即1,2,3,4,6,8,12,24。
(4)若一个数既没有完整的包含2^3也没有完整地包含3^1,比如1,2,4,那么另一个数必须为24,此时有3种。
到此为止,你发现除了(24,24)这种组合只在(3)中出现一次,其他情况均出现2次。若上面的总数为t,那么答案为(t+1)/2。

#include<bits/stdc++.h>
using namespace std;
#define nn 10000005//根号最大值就可以
bool vis[nn];//vis[i] = 0代表i是素数,否则不是
int prime[nn / 10];//保存1-(nn-5)范围内的素数,个数为m
long long n, m;
int a[nn / 10], b[nn / 10], t;
void Is_prime()//素数筛法
{
    long long i, j;
    memset(vis, 0, sizeof(vis));
    vis[1] = vis[0] = 1;
    m = 0;
    for(i = 2; i <= nn - 5; i++)
    {
        if(!vis[i])
        {
            prime[m++] = i;
            for(j = 2*i; j <= nn - 5; j = j + i)
                vis[j] = 1;
        }
    }
}
long long solve()
{
    long long x, y, i, j;
    long long ans = 0;
    for(i = 0; i < 1<<t; i++)//分解出t个素数,(1<<t)所有可能的情况
    {
        x = y = 1;
        for(j = 0; j < t; j++)
        {
            if(i&(1<<j)) x *= b[j] + 1;//包含n所以要+1,二进制为1对应的质素 组成有x个不同的数
            else y *= b[j];//二进制为0对应的质素 组成有y个不同的数
        }
        ans += x * y;//组合起来的情况为x*y
    }
    return (ans + 1) / 2;//返回所有情况
}
int main()
{
    int T, Case = 1;
    scanf("%d", &T);
    Is_prime();
    while(T--)
    {
        scanf("%lld", &n);
        long long mm = sqrt(n);
        if(mm * mm > n) mm--;//可加可不加,加比较保险,防止出意外
        t = 0;
        for(int i = 0; i < m && prime[i] <= mm; i++)//质因数分解
        {
            if(n % prime[i] == 0)
            {
                a[t] = prime[i];
                b[t] = 0;
                while(n % prime[i] == 0)
                {
                    b[t]++;
                    n /= prime[i];
                }
                t++;
            }
        }
        if(n != 1) {
            a[t] = n;
            b[t++] = 1;
        }//
        printf("Case %d: %lld\n", Case++, solve());
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值