Description
给出N,统计满足下面条件的数对(a,b)的个数:
1.1<=a < b<=N
2.a+b整除a*b
Input
一行一个数N
Output
一行一个数表示答案
Sample Input
15
Sample Output
4
HINT
数据规模和约定
Test N Test N
1 <=10 11 <=5*10^7
2 <=50 12 <=10^8
3 <=10^3 13 <=2*10^8
4 <=5*10^3 14 <=3*10^8
5 <=2*10^4 15 <=5*10^8
6 <=2*10^5 16 <=10^9
7 <=2*10^6 17 <=10^9
8 <=10^7 18 <=2^31-1
9 <=2*10^7 19 <=2^31-1
10 <=3*10^7 20 <=2^31-1
这个题应该是反演里比较上档次的题了…
我们来写一写推导
令 a=mx,b=my 则 a+b=m(x+y),a×b=xym2
m为a和b的最大公约数
有 a+b|a×b 即 m(x+y)|xym2
故 x+y|xym
假设 x<y
有 gcd(x,y)=1 ,所以 gcd(x+y,x)=gcd(x+y,y)=1
那么只能是 x+y|m
所以我们可以设 m=k(x+y)
既然有 1≤a<b≤n 那么得到 yk(x+y)≤n
所以对每组确定的x,y,符合条件的k有 ⌊ny(x+y)⌋ 个
显然可以有 y≤n√−1 ,所以枚举y的取值.这一步是 O(n√) 的.
这时x的取值必然是 1→y−1
直接枚举这样的x,y,当 gcd(x,y)=1 时计算答案, ans+=ny(x+y) 是正确的.可以得到50%的分数.
对于剩下的50%,我们可以考虑使用Mobius反演.
化到上面我们可以发现自己的问题转化成了求
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define MAXN 100010
#define LL long long
using namespace std;
int n;
bool not_prime[MAXN];
int prime[MAXN],top;
int phi[MAXN]={0,1},mu[MAXN]={0,1};
int divs[MAXN],Top;
LL ans,temp;
inline void check_prime()
{
for (int i=2;i<MAXN;i++)
{
if (!not_prime[i]) prime[++top]=i,phi[i]=i-1,mu[i]=-1;
for (int j=1;j<=top&&i*prime[j]<MAXN;j++)
{
not_prime[i*prime[j]]=1;
if (i%prime[j]==0)
{
mu[i*prime[j]]=0;
phi[i*prime[j]]=phi[i]*prime[j];
break;
}
else
{
mu[i*prime[j]]=-mu[i];
phi[i*prime[j]]=phi[i]*(prime[j]-1);
}
}
}
}
inline void solve(int x)//处理因子
{
Top=0;
for (int i=1;i*i<=x;i++)
if (x%i==0) divs[++Top]=i,divs[++Top]=x/i;
if (divs[Top]==divs[Top-1]) Top--;
sort(divs+1,divs+Top+1);
}
int main()
{
check_prime();
scanf("%d",&n);
int last;
for (LL i=1;i*(i+1)<=n;i++)
{
solve(i);
for (LL j=1;j<i&&i*(i+j)<=n;j=last+1)
{
last=min(n/(n/i/(i+j))/i-i,i-1);//kx(x+y)<=n x<y 已知y=i,求x
temp=0;
for (LL k=1;divs[k]<=last;k++) temp+=mu[divs[k]]*(last/divs[k]-(j-1)/divs[k]);
ans+=n/i/(i+j)*temp;//ans+=k*temp
}
}
printf("%lld\n",ans);
}