枚举每个质数,然后暴力算,TLE
换一种思路,改变枚举顺序
这样可以枚举n/T的取值,只需要预处理的前缀和就可以了。
因为1~n中大概有n/ln n个质数,每个质数平均会更新ln n次,所以暴力处理即可,总复杂度为O(n)。
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<iostream>
#define maxn 10000010
using namespace std;
int T,tot;
long long n,m;
int prime[maxn],mu[maxn],f[maxn];
long long sum[maxn];
bool vis[maxn];
long long cal(long long n,long long m)
{
if (n>m) swap(n,m);
long long ans=0,last;
for (long long i=1;i<=n;i=last+1)
{
last=min(n/(n/i),m/(m/i));
ans+=(n/i)*(m/i)*(sum[last]-sum[i-1]);
}
return ans;
}
int main()
{
scanf("%d",&T);
mu[1]=1;f[1]=0;sum[1]=0;
for (int i=2;i<=10000000;i++)
{
if (!vis[i])
{
prime[++tot]=i;
mu[i]=-1;
}
for (int j=1;j<=tot && i*prime[j]<=10000000;j++)
{
vis[i*prime[j]]=1;
if (i%prime[j]==0)
{
mu[i*prime[j]]=0;
break;
}
mu[i*prime[j]]=-mu[i];
}
}
for (int j=1;j<=tot;j++)
for (int i=1;i*prime[j]<=10000000;i++)
f[i*prime[j]]+=mu[i];
for (int i=2;i<=10000000;i++) sum[i]=sum[i-1]+f[i];
while (T--)
{
scanf("%lld%lld",&n,&m);
printf("%lld\n",cal(n,m));
}
return 0;
}