送个结论 d(n∗m)=∑d|n∑k|ne(gcd(d,k))
记 g(n)=∑ni=1⌊ni⌋
然后我们开始推
∑ni=1∑mj=1d(i∗j)=∑ni=1∑mj=1⌊ni⌋⌊mj⌋e(gcd(i,j))=∑ni=1∑mj=1⌊ni⌋⌊mj⌋∑d|i,d|jμ(d)=∑min(n,m)d=1μ(d)g(⌊nd⌋)g(⌊md⌋)
分块+预处理即可code:
#include
#include
#include
using namespace std;
int t,n,m,tot=0;
bool a[50001];
int prime[50001],mu[50001],s[50001],g[50001];
int calc(int maxn)
{
int i,last,ans=0;
for (i=1;i<=maxn;i=last+1)
{
last=maxn/(maxn/i);
ans+=(last-i+1)*(maxn/i);
}
return ans;
}
long long work(int n,int m)
{
int i,last;
long long ans=0;
if (n>m) swap(n,m);
for (i=1;i<=n;i=last+1)
{
last=min(n/(n/i),m/(m/i));
ans=ans+(long long)(s[last]-s[i-1])*(long long)(g[n/i])*(long long)(g[m/i]);
}
return ans;
}
void prework(int maxn)
{
int i,j;
mu[1]=1;
for (i=2;i<=maxn;++i)
{
if (!a[i])
{prime[++tot]=i; mu[i]=-1;}
for (j=1;j<=tot&&prime[j]*i<=maxn;++j)
{
a[prime[j]*i]=true;
if (i%prime[j]==0)
{
mu[i*prime[j]]=0;
break;
}
mu[i*prime[j]]=-mu[i];
}
}
for (i=1;i<=maxn;++i)
s[i]=s[i-1]+mu[i];
for (i=1;i<=maxn;++i)
g[i]=calc(i);
}
int main()
{
int i,minn;
long long ans;
prework(50000);
scanf("%d",&t);
while (t--)
{
scanf("%d%d",&n,&m);
printf("%lld\n",work(n,m));
}
}