【洛谷2257/BZOJ2820】YY的GCD(数论/莫比乌斯函数)

题目:

洛谷2257

预备知识:莫比乌斯定理(懵逼乌斯定理)

μ1=ϵ μ ∗ 1 = ϵ (证bu明hui略zheng)
其中(我校学长把 ϵ(x) ϵ ( x ) 叫单位函数但是为什么我没百度到qwq)

ϵ(x)={10x=1x1 ϵ ( x ) = { 1 x = 1 0 x ≠ 1

μ(x)=10(1)kx=1p使p2|xkx μ ( x ) = { 1 x = 1 0 存 在 质 数 p 使 p 2 | x ( − 1 ) k k 是 x 质 因 数 的 个 数

那个 是迪利克雷卷积,换成人话就是
ϵ(n)=d|nμ(d)

我觉得用这种方式理解莫比乌斯定理比设两个函数容易

分析:

这题莫比乌斯定理的经典用例。
本文中默认 N>M N > M
默认 p p 是质数

显然如果gcd(i,j)=p,那么 gcd(ip,jp)=1 g c d ( i p , j p ) = 1
那么题目所求可以转换成下面的式子

pNiN/pjM/pϵ(gcd(i,j)) ∑ p N ∑ i N / p ∑ j M / p ϵ ( g c d ( i , j ) )

其中(我校学长把这个叫单位函数但是我没百度到qwq)
ϵ(x)={10x=1x1 ϵ ( x ) = { 1 x = 1 0 x ≠ 1

根据莫比乌斯反演定理,上面的式子就可以变成

p=2NiN/pjM/pd|gcd(i,j)μ(d) ∑ p = 2 N ∑ i N / p ∑ j M / p ∑ d | g c d ( i , j ) μ ( d )

改变一下枚举顺序,用 di d · i 表示原来的 i i d·j表示原来的 j j ,得到
p=2NdN/piN/pdjM/pdμ(d)

可以发现 μ(d) μ ( d ) i i j没半毛钱关系,仅仅是乘上 i i j可以取的值的数量
也就是
p=2NdN/pμ(d)NpdMpd ∑ p = 2 N ∑ d N / p μ ( d ) ∗ ⌊ N p d ⌋ ∗ ⌊ M p d ⌋

T=pd T = p d ,枚举T,上式可变成
TNp|Tμ(Tp)NTMT ∑ T N ∑ p | T μ ( T p ) ∗ ⌊ N T ⌋ ∗ ⌊ M T ⌋

g(x)=p|xμ(xp) g ( x ) = ∑ p | x μ ( x p )

则上式就是
TNNTMTg(T) ∑ T N ⌊ N T ⌋ ∗ ⌊ M T ⌋ ∗ g ( T )

现在考虑如何求 g(x) g ( x ) 这个函数。
首先,对于任意质数 p p ,显然g(p)=μ(1)=1
然后,对于任意合数 n=kp0 n = k p 0 p0 p 0 是质数) g(n) g ( n ) 中显然存在 μ(np0) μ ( n p 0 ) 也就是 μ(k) μ ( k ) 这一项
p0|k p 0 | k ,也就是 p20|n p 0 2 | n ,对于任意 p|k p | k pp0 p ≠ p 0 μ(np) μ ( n p ) 中一定有 p20 p 0 2 这个质数平方因子。根据 μ(x) μ ( x ) 的定义, μ(np)=0 μ ( n p ) = 0
所以此时 g(n)=μ(k) g ( n ) = μ ( k )
p0 p 0 不能整除 k k ,对于任意p|k μ(np) μ ( n p ) μ(kp) μ ( k p ) 多了 p0 p 0 这个质因子。根据 μ(x) μ ( x ) 的定义 μ(np)=μ(kp) μ ( n p ) = − μ ( k p )
所以此时 g(n)=g(k)+μ(k) g ( n ) = − g ( k ) + μ ( k )
总结一下
g(x)=1μ(k)g(k)+μ(k)xx=kppkx=kppk g ( x ) = { 1 x 是 质 数 μ ( k ) x = k p 且 p 能 整 除 k − g ( k ) + μ ( k ) x = k p 且 p 不 能 整 除 k

显然这个函数可以用线性筛求
TNNTMTg(T) ∑ T N ⌊ N T ⌋ ∗ ⌊ M T ⌋ ∗ g ( T )

再来看这个式子,既然 g(T) g ( T ) 可以直接预处理并 O(1) O ( 1 ) 查询,那么计算这个式子的时间复杂度就是枚举 T T 的复杂度O(N)

我会做啦!

别急,这题还有 T T 组询问,所以复杂度是O(不可过)O(NT),这个过不了。
但是我们可以发现 NTMT ⌊ N T ⌋ ∗ ⌊ M T ⌋ T T 的一段区间内是不变的,所以可以给g(T)算个前缀和然后分段计算,据说复杂度是 O(NT) O ( N T ) (我不会证),这样就可以过了

代码:

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;

namespace zyt
{
    typedef long long ll;
    const int N = 1e7 + 10, M = 7e5;
    bool mark[N];
    int cnt, prime[M], phi[N], mu[N];
    ll g[N];
    void init()
    {
        mu[1] = 1;
        for (int i = 2; i < N; i++)
        {
            if (!mark[i])
                prime[cnt++] = i, mu[i] = -1, g[i] = 1;
            for (int j = 0; j < cnt && (ll)i * prime[j] < N; j++)
            {
                int k = i * prime[j];
                mark[k] = true;
                if (i % prime[j] == 0)
                {
                    mu[k] = 0;
                    g[k] = mu[i];
                    break;
                }
                else
                {
                    mu[k] = -mu[i];
                    g[k] = -g[i] + mu[i];
                }
            }
        }
        for (int i = 2; i < N; i++)
            g[i] += g[i - 1];
    }
    void work()
    {
        int T;
        init();
        scanf("%d", &T);
        while (T--)
        {
            int n, m, pos = cnt;
            ll ans = 0;
            scanf("%d%d", &n, &m);
            if (n > m)
                swap(n, m);
            for (int t = 1; t <= n;)
            {
                int tmp = min(n / (n / t), m / (m / t));
                ans += (g[tmp] - g[t - 1]) * (n / t) * (m / t);
                t = tmp + 1;
            }
            printf("%lld\n", ans);
        }
    }
}
int main()
{
    zyt::work();
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值