训练4.05(Pairs Forming LCM 分解素因子)

题目:

B - Pairs Forming LCM

Time Limit:2000MS Memory Limit:32768KB

Description

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 ≤ 10^14).

Output

For each case, print the case number and the value returned by the function ‘pairsFormLCM(n)’.

题意:读一下题目给的代码,给定n,求在该范围内取两个数I,J,且I<=J,若I,J的最小公倍数等于n,计数+1,最后输出计数值。
然鹅n过于大了,所以暴力铁定不可行。

题解:
由于任何一个数都可以分解为若干个质数的乘积,故:

a = p1 ^ a1 * p2 ^ a2 *…*pk ^ ak

b = p1 ^ b1 * p2 ^ b2 *…*pk ^ bk

这里的a,b是该代码中的I,J;.
p1,p2,…pk是质数;
a1,a2,…ak;b1,b2,…bk;c1,c2…,ck取值非负

然后,最大公因数和最小公倍数也是可以通过以上数据条件得出:

gcd(a,b)=p1 ^ min(a1,b1) * p2 ^ min(a2,b2) *…*pk ^ min(ak,bk)

lcm(a,b)=p1 ^ max(a1,b1) * p2 ^ max(a2,b2) *…*pk ^ max(ak,bk)

对比一下题目的要求,即 lcm(a,b)==n。而n又可以分解为:

n = p1 ^ c1 * p2 ^ c2 *…*pk ^ ck

所以得出一个小结论:

c1=max(a1,b1),c2=max(a2,b2),c3=max(a3,b3),…ck=max(ak,bk)

故:在ai,bi中必须有一个是ci,另一个在其取值范围内随意取一个就行。如此对 ai,bi 似乎就会有2(ci+1)种分派方式。

然鹅举个例子,假如ci=3,那么
(ai,bi)的所有分派方式按上面得出来的,会是:{(0,3),(1,3),(2,3),(3,3),(3,0),(3,1),(3,2),(3,3)}。

发现了吗,有两个(3,3)。所以 2(ci+1)种分配中会产生2个相同的(n,n),所以不重合的组合有2(ci+1)-1,即2ci+1种。

那么现在问题似乎变简单了,直接将 n 分解后的每个质因子pi的个数(即ci)乘2后再+1,再累乘起来。
最后累乘的结果+1,再除2就是答案。
具体见代码。

为什么+1除2呢?
又得举例子了,比如k是2,n = p1 ^ 2 * p2 ^ 3,那么

a1,b1:
A={(0,2),(1,2),(2,2),(2,0),(2,1)} ;

a2,b2:
B={(0,3),(1,3),(2,3),(3,3),(3,0),(3,1),(3,2)}。

在A中取一个数对,在B中也取一个数对,会发现,比如,取法(0,2)、(0,3),和取法(2,0)、(3,0),得出来的两个数只是颠倒了而已,在要求I<=J的大前提下,就需要去掉其中一个。
但是得出(n,n)的取法就只有一种。所以去掉不符合的操作,就是给最后累乘的结果加个1,再除以2。

然后你以为这样就可以了吗??看看空间限制
这道题让我学会了如何计算空间 -。-

碎碎念…
一开始一直报RE,所以就没想过是空间限制问题,一直在想哪越界了或者哪里除以0了,结果一直找不到。最后训练结束看别人AC的代码,发现和人家代码逻辑基本一样,最后的最后发现是空间不同,就学着把质数数组大小改小一点,就终于报MLE。再仔细一看发现人家的AC代码中,vis数组开的是bool,我的是int。最后又把开始RE的代码也改了数组空间,发现也可以AC。

长知识了Y
int一般是4B,即32位,bool是1B,
实在记不着,就直接上代码:

cout<<sizeof(int)<<" "<<sizeof(bool)<<"\n"; 

好啦,上题目代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>
#include<string>
#include<queue> 
#include<cmath>
#include<cstring>
using namespace std;
#define ll long long
#define pii pair<int,int>
#define pll pair<ll,ll>
const int maxn=1e7+6;
bool vis[maxn];
const int N=7e5;//这里注意!!
int pri[N];
int cnt; 
void ToPrime(){
    cnt=0;
    memset(vis,0,sizeof(vis));
    for(int i=2;i<maxn;i++){
        if(!vis[i]){
            pri[cnt++]=i;
            for(int j=i+i;j<maxn;j+=i){
                vis[j]=1;
            }
        }
    }
}
int main(){
	ToPrime();
	int T,t;
	t=0;
	scanf("%d",&T);
	while(T--){
		t++;
		ll n;
		scanf("%lld",&n);
		ll ans=1;
		for(int i=0;i<cnt&&(ll)pri[i]*(ll)pri[i]<=n;i++){
            if(n%(ll)pri[i]==0){
                ll c=0;
                while(n%(ll)pri[i]==0){
                    n/=(ll)pri[i];
                    c++;
                }
                ans*=(2*c+1);
            }
        }
        if(n!=1)ans=ans*3;
        printf("Case %d: %lld\n",t,(ans+1)/2);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值