链接
http://www.lydsy.com/JudgeOnline/problem.php?id=2818
题解
可以欧拉函数
O(NlogN)
,也可以莫比乌斯反演
O(NN√logN)
。别问我为什么能过,我也不知道…
欧拉函数的话,直接枚举素数然后欧拉函数前缀和。
莫比乌斯反演:
枚举素数
d
,令
∑i=1n∑j=1n[gcd(i,j)=1]
即
∑i=1n∑j=1n∑d|gcd(i,j)μ(d)
继续
∑d=1nμ(d)∑i=1⌊nd⌋∑j=1⌊nd⌋1
即
∑d=1nμ(d)⌊nd⌋⌊nd⌋
然后就可以分块了。
代码
//欧拉函数
#include <cstdio>
#include <algorithm>
#define maxn 10000000
#define ll long long
using namespace std;
int prime[maxn/10+10], phi[maxn+10], N;
ll s[maxn+10];
void init()
{
int i, j;
for(i=1;i<=N;i++)phi[i]=i;
for(i=2;i<=N;i++)
{
if(phi[i]==i)
{
prime[++prime[0]]=i;
for(j=i;j<=N;j+=i)phi[j]=phi[j]-phi[j]/i;
}
}
for(i=1;i<=N;i++)s[i]=s[i-1]+phi[i];
}
int main()
{
int i;
ll ans=0;
scanf("%d",&N);
init();
for(i=1;i<=prime[0] and prime[i]<=N;i++)
ans+=(s[N/prime[i]]<<1)-1;
printf("%lld",ans);
return 0;
}
//莫比乌斯反演
#include <cstdio>
#include <algorithm>
#define ll long long
#define maxn 50000
using namespace std;
ll mu[maxn+10], prime[maxn+10], f[maxn+10], mark[maxn+10], s[maxn+10];
void init()
{
ll i, j;
s[1]=mu[1]=1;
for(i=2;i<=maxn;i++)
{
if(!mark[i])prime[++prime[0]]=i,mu[i]=-1;
for(j=1;j<=prime[0] and i*prime[j]<=maxn;j++)
{
mark[i*prime[j]]=1;
if(i%prime[j]==0)
{
mu[i*prime[j]]=0;
break;
}
mu[i*prime[j]]=-mu[i];
}
s[i]=s[i-1]+mu[i];
}
for(i=1;i<=maxn;i++)
for(j=i;j<=maxn;j+=i)f[j]++;
for(i=1;i<=maxn;i++)f[i]+=f[i-1];
}
ll calc(ll n, ll m)
{
ll ans=0, i, last;
if(n>m)swap(n,m);
for(i=1;i<=n;i=last+1)
{
last=min(n/(n/i),m/(m/i));
ans+=(s[last]-s[i-1])*f[n/i]*f[m/i];
}
return ans;
}
int main()
{
ll T, N, M;
init();
scanf("%lld",&T);
while(T--)
{
scanf("%lld%lld",&N,&M);
printf("%lld\n",calc(N,M));
}
return 0;
}