有一个非常神的东西:
d(n∗m)=∑i|n∑j|mgcd(i,j)==1
并不知道怎么证明……然后有了这个式子后面就很简单了!
然后
Ans=∑i=1n∑j=1m⌊ni⌋⌊mj⌋(gcd(i,j)==1)
=∑i=1n∑j=1m∑d|i,d|ju(d)⌊ni⌋⌊mj⌋
=∑d=1min(n,m)u(d)∑i=1⌊nd⌋∑j=1⌊md⌋⌊nd∗i⌋⌊md∗j⌋
=∑d=1min(n,m)u(d)(∑i=1⌊nd⌋⌊nd∗i⌋∑j=1⌊md⌋⌊md∗j⌋)
=∑d=1min(n,m)u(d)∗f(⌊nd⌋)∗f(⌊md⌋)
然后可以发现f(x)即为d(x)的前缀和,d(x)可以线性筛出来
(当年参加省选,这个题打表就有20分……我感觉暴力第一个点就会超时然后就弃疗了
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<iostream>
#include<algorithm>
#define ll long long
#define N 50050
#define mx 1e9
using namespace std;
int sc()
{
int i=0,f=1; char c=getchar();
while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9')i=i*10+c-'0',c=getchar();
return i*f;
}
int a[N],prime[N];
int mu[N],d[N],low[N];
void pre()
{
int top=0;
mu[1]=low[1]=d[1]=1;
for(int i=2;i<N;i++)
{
if(!a[i])
{
low[i]=prime[++top]=i;
d[i]=2;mu[i]=-1;
}
for(int j=1;prime[j]*i<N;j++)
{
a[i*prime[j]]=1;
if(i%prime[j]==0)
{
low[i*prime[j]]=low[i]*prime[j];
if(low[i]==i)
d[i*prime[j]]=d[i]+1;
else
d[i*prime[j]]=d[i/low[i]]*d[prime[j]*low[i]];
break;
}
low[i*prime[j]]=prime[j];
d[i*prime[j]]=d[i]*2;
mu[i*prime[j]]=-mu[i];
}
}
for(int i=2;i<N;i++)mu[i]+=mu[i-1],d[i]+=d[i-1];
}
long long solve(int n,int m)
{
long long ans=0;
if(n>m)swap(n,m);
for(int i=1,last;i<=n;i=last+1)
{
last=min(n/(n/i),m/(m/i));
ans+=(ll)(mu[last]-mu[i-1])*d[n/i]*d[m/i];
}
return ans;
}
int main()
{
pre();
int T=sc();
while(T--)
{
int x=sc(),y=sc();
printf("%lld\n",solve(x,y));
}
return 0;
}