『莫比乌斯反演』YY的GCD

题目描述

给定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) f(i) = d=1in g(id)g(i) = d=1inμ(d)f(id)

我们对于所求的答案 a n s ans 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 ans = ig(i) = id=1inμ(d)idnidm

然后再对这一个 a n s ans 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}) ans = TTnTmdTμ(dT).

我们另 t ( i )   =   ∑ d ∣ T μ ( T d ) t(i)\ =\ \sum_{d|T}μ(\frac{T}{d}) t(i) = dTμ(dT),我们只要能够快速的求出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 i枚举的是每一个质数, j j j枚举的是每一个数中, i ∗ p r i m e j i*prime_j iprimej中除去 p r i m e j prime_j primej的值。

代码如下:

#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;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值