题目描述
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
",solve(n));
}
return 0;
}