有一个M * N的表格,行与列分别是1 - M和1 - N,格子中间写着行与列的最大公约数Gcd(i, j)(1 <= i <= M, 1 <= j <= N)。
例如:M = 5, n = 4。
1 2 3 4 5
1 1 1 1 1 1
2 1 2 1 2 1
3 1 1 3 1 1
4 1 2 1 4 1
给出M和N,求这张表中有多少个质数。
输入
第1行:一个数T,表示后面用作输入测试的数的数量。(1 <= T <= 1000) 第2 - T + 1行:每行2个数M,N,中间用空格分隔,表示表格的宽和高。(1 <= M, N <= 5 * 10^6)
输出
共T行,每行1个数,表示表格中质数的数量。
输入样例
2 10 10 100 100
输出样例
30 2791
题目要求的是:
设F(x)为gcd(i,j)为x的倍数(即gcd(i,j)==kx)时,方案的个数;f(x)为gcd(i,j)=x事,方案的个数。
很明显:
显然有公式:
反演得到:
要求的答案是:
即(p代表质数)
转换枚举变量,枚举d,将d提到前面:
到现在公式就化简完了,它的含义是枚举d求出F(d),对于每个d我们还要求出它的全部质因子p的的和。
我们可以先预处理筛出给出数据范围内的质数和莫比乌斯函数的值。
然后,设,可以枚举每我们个质数,然后将它的贡献加给d即可。
然后,对于F(d),显然可以除法分块求和,这里因为分块,所以要把sum数组再求一遍前缀和。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 5e6+10;
int prime[N],mu[N],sum[N],cnt;
bool vis[N];
void Init()
{
cnt=0;
mu[1]=1;//不要忘记这个
for(int i=2;i<=N-10;i++)
{
if(!vis[i])
{
prime[cnt++]=i;
mu[i]=-1;
}
for(int j=0;j<cnt&&(ll)i*prime[j]<=N-10;j++)
{
vis[i*prime[j]]=1;
if(i%prime[j])
{
mu[i*prime[j]]=-mu[i];
}
else
{
mu[i*prime[j]]=0;
break;
}
}
}
//求出每个质数对答案的贡献
for(int i=0;i<cnt;i++)
for(int j=1;(ll)j*prime[i]<=N-10;j++)
{
sum[j*prime[i]]+=mu[j];
}
//前缀和
for(int i=1;i<=N-10;i++)
sum[i]+=sum[i-1];
return ;
}
int main(void)
{
Init();
// for(int i=0;i<10;i++)
// cout<<prime[i]<<" "<<i<<" "<<mu[i]<<endl;
int t;
int n,m,M;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
M=min(n,m);
ll ans=0;
int r;
//除法分块求和
for(int l=1;l<=M;l=r+1)
{
r=min(min(n/(n/l),m/(m/l)),M);
ans+=(ll)(n/l)*(m/l)*(sum[r]-sum[l-1]);
}
printf("%lld\n",ans);
}
return 0;
}