Gcd表中的质数 51Nod

来源:互联网 发布:淘宝客 部分退款 编辑:程序博客网 时间:2024/05/22 13:20

有一个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)=nxmx

f(n)gcd(x,y)为n的数量,则:
F(n)=n|df(d)

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

所以:
ans=pmin(n,m)p|dμ(dp)F(d)

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

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

#include<cstdio>#include<iostream>#include<algorithm>#include<cstring>#include<cmath>#include<vector>#define N 5000005using namespace std;typedef long long  ll;vector<int> prime;bool pri[N];int mu[N];int sum[N];int cal[N];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;}