解题思路:
题目大意即为求:
∑p∈P∑i=1n∑j=1m[gcd(i,j)=p]
原式 =∑p∈P∑i=1np∑j=1mp[gcd(i,j)=1]
=∑p∈P∑i=1np∑j=1mpϵ(gcd(i,j))
=∑p∈P∑i=1np∑j=1mp∑d|gcd(i,j)μ(d)
=∑p∈P∑i=1np∑j=1mp∑d|i,d|jμ(d)
=∑p∈P∑d∑i=1npd∑j=1mpdμ(d)
=∑p∈P∑dμ(d)⌊npd⌋⌊mpd⌋
这里我们设 T=pd ,则 d=Tp ,现在改为枚举T,则
原式 =∑T⌊nT⌋⌊mT⌋∑p∈P,p|Tμ(Tp) 。
令
g(x)=∑p∈P,p|xμ(xp)
那么关键在于求 g(x) 的前缀和。
考虑线性筛法。
对于一个质数 p0
当 p0|x ,则 μ(p0x)=0 ,那么当 p≠p0 ,有 mu(p0xp)=0 ,所以此时
g(p0x)=∑p∈P,p|p0xμ(p0xp)=μ(x)
当 p0 不整除 x ,则有
g(p0x)=∑p∈P,p|xμ(xp)∗μ(p0)+μ(p0xp0)
即
g(p0x)=−g(x)+μ(x)
。
所以可以线性筛出 g(x) ,那么本题就可以 O(n) 预处理, O(n√) 查询了。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<ctime>
#include<vector>
#include<set>
#include<complex>
#define ll long long
using namespace std;
int getint()
{
int i=0,f=1;char c;
for(c=getchar();(c<'0'||c>'9')&&c!='-';c=getchar());
if(c=='-')f=-1,c=getchar();
for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
return i*f;
}
const int N=1e7+5,INF=0x3f3f3f3f;
int T,n,m,pn;
int pri[N],mu[N],g[N],sum[N];
void sieve()
{
memset(mu,INF,sizeof(mu));
mu[1]=1;
for(int i=2;i<=1e7;i++)
{
if(mu[i]==INF)pri[++pn]=i,mu[i]=-1,g[i]=1;
for(int j=1;j<=pn;j++)
{
ll k=1ll*i*pri[j];
if(k>1e7)break;
if(i%pri[j]==0)
{
mu[k]=0;
g[k]=mu[i];
break;
}
else
{
mu[k]=-mu[i];
g[k]=mu[i]-g[i];
}
}
sum[i]=sum[i-1]+g[i];
}
}
int main()
{
//freopen("lx.in","r",stdin);
//freopen("lx.out","w",stdout);
sieve();
T=getint();
while(T--)
{
n=getint(),m=getint();
if(n>m)swap(n,m);
ll ans=0;
for(int i=1,j;i<=n;i=j+1)
{
j=min(n/(n/i),m/(m/i));
ans+=1ll*(n/i)*(m/i)*(sum[j]-sum[i-1]);
}
printf("%lld\n",ans);
}
return 0;
}