1459: GCD与LCM [数学](基础数论)

题目描述

Ocean某天遇到了一个很简单的数学题,但是他的大脑已经超负荷了。现在请你帮帮他吧:

已知D = GCD(x, y) + LCM(x, y),求合法组合(x, y)总数,其中x > 0且y > 0。

输入

第一行输入一个整数T,代表有T组测试数据。

接下来每行输入一个整数D。

注:1<=T,D<=1051<=T,D<=105

输出

对每组测试数据,输出一个整数代表可能出现的组合数。

样例输入

3
2
3
4

样例输出

1
2
3

提示

 

GCD:最大公约数

LCM:最小公倍数

样例中:

若D=2,可能有①x=1,y=1;

若D=3,可能有①x=1,y=2②x=2,y=1

若D=4,可能有①x=1,y=3②x=2,y=2③x=3,y=1

D = L + G 。 

则 x = G * a . y = G * b . 
        x * y = G * G * a * b 
        x * y = G * L . 
        联立上式子: a * b = L / G = (D-G) / G = D/G -1 
        关键式子 : a * b = D / G -1 (注意这里D/G 一定是整数(因为L/G 一定是整数),同时a和b一定是互质的(如果不是互  质的话, 就不能够满足G为最大公因数) 
就是针对这个式子,开始化简,减少时间复杂度。 
令 a * b = z . 
z = D/ G - 1 .对于式子的右边值,我们可以遍历1-sqrt(D), 关键是 知道了a * b的值z,之前已经用了sqrt的时间复杂度,接下来的只能够用O(1)的了,我们怎么用o(1)处理 有多少对互质的数相乘为z呢? 这里想起来之前写的 一道题,可以o(n)预处理求出n以内任意数的 因子个数 ,感觉可以从这个筛法中取得 。 

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e5+11;
#define LL long long

LL gcd(LL a,LL b){ return b==0?a:gcd(b,a%b); }
int num[MAXN+2];
int fac[MAXN+2][250],cnt[MAXN+2];
void init(){
    num[1]=1; num[0]=0;
    for(LL i=1;i*i<=MAXN;i++){// 这里可以同时筛选 两个
        for(LL j=i;j*i<=MAXN;j++){
            fac[i*j][cnt[i*j]++]=j;
            if(i==j) continue;
            if(gcd(i,j)==1) num[i*j]+=2;
        }
    }
}

LL solve(int n){
    LL ans=0;
    for(int i=0;i<cnt[n];i++){
        int G=fac[n][i];
        ans+=num[n/G-1];
        if(n/G!=G) ans+=num[G-1];
    }
    return ans;
}

int main(){
    init();
    int T;scanf("%d",&T);
    while(T--){
        int n;scanf("%d",&n);
        if(n==1) puts("0");
        else printf("%lld\n",solve(n));
    }
return 0;
}


 

 

posted @ 2018-04-10 19:22  Nlifea  阅读(278)  评论(0编辑  收藏  举报