我们一起来看看这个问题,说实话,当时一看到这题的时候我怕了,不过想通后那叫一个爽啊!
题意:给你一个N,求满足1/X+1/Y=1/N的X,Y种类数
当你看到这题的时候,你会怎么做呢?
当时我看到第一反应就是先化简,因为1/X这个数肯定是比1小的小数,这个精度问题是个大问题,而且两个小数相加也不会完全等于那个小数。所以,想办法划成整数关系式。
两边同乘XY,得 Y+X=XY/N
转换,得 N=XY/(X+Y)
然后直接求吗?不不不,N是个整数,难道你就能保证右边除出来是整数吗?!
然后,绞尽脑汁,沉思,深呼吸,学下一休哥,忽然想到了!
你可以保证,当两个分数相加要等于一个分数时,两个分数的分母肯定都大于这个分数的分母,也就是X,Y>N
我们可以假设,X=N+u,Y=N+v, 其中u,v肯定都是大于0的整数
然后代入,简化出来,不可思议,N^2=uv
而一个整数N都是可以拆成 N=p1^a1 * p2^a2 *……*pn^an 的形式
此时N拆成两个数相乘的形式的种类个数是(a1+1)*(a2+1)*……*(an+1) 【不细讲了,你可以自己去写几个找找规律】
那么N^2=p1^2a1 * p2^2a2 *……*pn^2an
此时,种类数即为(2a1+1)*(2a2+1)*……*(2an+1)
这个问题就迎刃而解了,接下来就是求底和幂的事情了。
需要特别注意的是,当N<2时,是无法整数拆分的,也就是种类数为0
#include <iostream>
#include <cmath>
using namespace std;
#define MAXI 20
struct yinshu
{
int di;
int mi;
};
struct Div
{
int xiangshu;
int xdi[MAXI];
int xmi[MAXI];
};
Div getpN(int m);
void vout(Div pN);
yinshu getOne(int yin,int& m);
int main()
{
int ncase,n,i;
cin>>ncase;
Div pN;
while(ncase--)
{
cin>>n;
pN=getpN(n);
vout(pN);
}
return 0;
}
Div getpN(int m)
{
int i,j;
Div pans;
yinshu x;
if(m<2)
{
pans.xdi[0]=1;
pans.xmi[0]=0;
pans.xiangshu=0;
return pans;
}
int uplimit=(int)sqrt(m*1.0);
i=2;
j=0;
while(i<=uplimit)
{
if(m%i==0)
{
x=getOne(i,m);
pans.xdi[j]=x.di;
pans.xmi[j]=x.mi;
j++;
uplimit=(int)sqrt(1.0*m);
}
i++;
}
if(m>1)
{
pans.xdi[j]=m;
pans.xmi[j]=1;
j++;
}
pans.xiangshu=j;
return pans;
}
void vout(Div pN)
{
int i;
int ans=1;
for(i=0;i<pN.xiangshu;i++)
{
ans*=(pN.xmi[i]*2+1);
}
cout<<ans<<endl;
}
yinshu getOne(int yin,int& m)
{
yinshu x;
x.di=yin;
x.mi=0;
while(m%yin==0)
{
x.mi++;
m/=yin;
}
return x;
}