您的位置:首页 > 其它

Gcd表中的质数 51Nod - 1192 (莫比乌斯反演)

2017-10-21 01:48 531 查看
有一个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,求这张表中有多少个质数。

Input

第1行:一个数T,表示后面用作输入测试的数的数量。(1 <= T <= 1000)

第2 - T + 1行:每行2个数M,N,中间用空格分隔,表示表格的宽和高。(1 <= M, N <= 5 * 10^6)

Output

共T行,每行1个数,表示表格中质数的数量。

Sample Input

2

10 10

100 100

Sample Output

30

2791

莫比乌斯的经典哦

设F(n)为gcd(x,y)是n的倍数的数量,易知F(x)=nx∗mx

设f(n)为gcd(x,y)为n的数量,则:

F(n)=∑n|df(d)

所以反演得:f(n)=∑n|dμ(dn)F(d)

所以:ans=∑p为素数min(n,m)∑p|dμ(dp)F(d)

转换枚举变量(固定套路):ans=∑d=1min(n,m)F(d)∑p|dp为素数μ(dp)

设sum(x)=∑p为素数p|dμ(dp),把sum这个数组预处理出来即可,但是超时了,那分块就好了,把sum处里成前缀和即可

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#define N 5000005
using namespace std;
typedef long long  ll;
vector<int> prime;
bool pri
;
int mu
;
int sum
;
int cal
;
void init()
{
memset(pri,true,sizeof(pri));
mu[1]=1;
for(int i=2;i<N;i++)
{
if(pri[i])
{
prime.push_back(i);
mu[i]=-1;
}
for(int j=0;j<prime.size()&&i*prime[j]<N;j++)
{
pri[i*prime[j]]=false;
if(i%prime[j])
mu[i*prime[j]]=-mu[i];
else
{
mu[i*prime[j]]=0;
break;
}
}
}
//cout<<sum[1]<<endl;
for(int i=0;i<prime.size();i++)
for(int j=prime[i];j<N;j+=prime[i])
sum[j]+=mu[j/prime[i]];//筛法筛出sum的值
for(int i=1;i<N;i++)//处理成前缀和
cal[i]=cal[i-1]+sum[i];
}
int main()
{
int t;
init();
int n,m;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
int limit=min(n,m);
ll ans=0;
int last;
for(int i=1;i<=limit;i=last+1)
{
last=min(n/(n/i),m/(m/i));
ans+=(ll)(n/i)*(m/i)*(cal[last]-cal[i-1]);//分块
}
/*for(int i=1;i<=limit;i++)
ans+=(ll)(n/i)*(m/i)*sum[i];*///这种写法t了,才想着要分块
cout<<ans<<endl;
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: