『莫比乌斯反演』YY的GCD

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Ronaldo7_ZYB/article/details/89524946

题目描述

给定N, M,求1<=x<=N, 1<=y<=M且gcd(x, y)为质数的(x, y)有多少对。

其中数据加强,N和M将近1000w。

题解

根据莫比乌斯反演公式,则一定有:

f ( i )   =   d = 1 n i   g ( i d ) &ThickSpace; &ThickSpace; g ( i )   =   d = 1 n i μ ( d ) f ( i d ) f(i)\ =\ \sum_{d=1}^{\lfloor \frac{n}{i}\rfloor}\ g(i*d)\iff g(i)\ =\ \sum_{d=1}^{\lfloor \frac{n}{i}\rfloor}μ(d)*f(i*d)

我们对于所求的答案 a n s ans ,有:
a n s   =   i g ( i )   =   i d = 1 n i μ ( d ) n i d m i d ans\ =\ \sum_i g(i)\ =\ \sum_i \sum_{d=1}^{\lfloor \frac{n}{i}\rfloor}μ(d)\lfloor \frac{n}{i*d} \rfloor \lfloor \frac{m}{i*d}\rfloor

然后再对这一个 a n s ans 算式变形一下:用T来枚举这一份i*d,则有:
a n s   =   T n T m T d T μ ( T d ) ans\ =\ \sum_T \lfloor \frac{n}{T} \rfloor \lfloor \frac{m}{T}\rfloor \sum_{d|T}μ(\frac{T}{d}) .

我们另 t ( i )   =   d T μ ( T d ) t(i)\ =\ \sum_{d|T}μ(\frac{T}{d}) ,我们只要能够快速的求出T(i)就能够很快的得到答案。

我们可以在线性筛结束的时候求解这一个t(i),大致是这样的:

    for (int i=1;i<=m;++i)
        for (int j=1;prime[i]*j<=n;++j) 
            f[prime[i]*j] += Miu[j];

在这其中, i i 枚举的是每一个质数, j j 枚举的是每一个数中, i p r i m e j i*prime_j 中除去 p r i m e j prime_j 的值。

代码如下:

#include <bits/stdc++.h>

using namespace std;

const int N = 10000015;

int f[N];
int sum[N];
int Miu[N];
int vis[N];
int prime[N];

void Get_Miu(int n)
{
    int m = 0;
    Miu[1] = 1;
    for (int i=2;i<=n;++i)
    {
        if (vis[i] == 0) prime[++m] = i, Miu[i] = -1;
        for (int j=1;j<=m && i*prime[j]<=n;++j)
        {
            vis[i*prime[j]] = 1;
            if (i%prime[j] == 0) break;
            Miu[i*prime[j]] = -Miu[i];
        }
    }
    
    for (int i=1;i<=m;++i)
        for (int j=1;prime[i]*j<=n;++j) 
            f[prime[i]*j] += Miu[j];
    return;
}

void Get_sum(int n)
{
    for (int i=1;i<=n;++i)
        sum[i] = sum[i-1]+f[i];
    return;
}

void work(void)
{
    int n,m,j;
    long long ans = 0;
    scanf("%d %d",&n,&m);
    for (int i=1;i<=min(n,m);i=j+1) 
    {
        j = min(n/(n/i),m/(m/i));
        ans += (long long)(sum[j]-sum[i-1])*(n/i)*(m/i); 
    }
    printf("%lld\n",ans);
    return;
}

int main(void)
{
    Get_Miu(N-10);
    Get_sum(N-10);
    int n;
    cin>>n;
    while (n -- ) work();
    return 0;
} 

猜你喜欢

转载自blog.csdn.net/Ronaldo7_ZYB/article/details/89524946