P2257 莫比乌斯反演 + 整除分块

题意

传送门 P2257 YY的GCD

题解

∑ p m i n ( n , m ) ∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) = p ] \sum\limits_{p}^{min(n,m)}\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}[gcd(i,j)=p] pmin(n,m)i=1nj=1m[gcd(i,j)=p],不易直接求解,考虑 d ∣ g c d ( i , j ) d|gcd(i,j) dgcd(i,j) 的关系,设
F ( d ) = ∑ d ∣ k f ( k ) F(d)=\sum\limits_{d|k}f(k) F(d)=dkf(k) 莫比乌斯反演,得到
f ( p ) = ∑ p ∣ d μ ( d p ) F ( d ) f(p)=\sum\limits_{p|d}\mu(\frac{d}{p})F(d) f(p)=pdμ(pd)F(d)
则答案为 ∑ p m i n ( n , m ) ∑ p ∣ d μ ( d p ) ⌊ n d ⌋ ⌊ m d ⌋ \sum\limits_{p}^{min(n,m)}\sum\limits_{p|d}\mu(\frac{d}{p})\lfloor \frac{n}{d}\rfloor \lfloor \frac{m}{d} \rfloor pmin(n,m)pdμ(pd)dndm 直接枚举素数加整除分块复杂度为 O ( T n × n log ⁡ n ) O(T\sqrt n\times \frac{n}{\log n}) O(Tn ×lognn),考虑优化,交换求和次序
∑ d = 1 m i n ( n , m ) ⌊ n d ⌋ ⌊ m d ⌋ ∑ p ∣ d μ ( d p ) \sum\limits_{d=1}^{min(n,m)}\lfloor \frac{n}{d} \rfloor \lfloor \frac{m}{d} \rfloor\sum\limits_{p|d} \mu(\frac{d}{p}) d=1min(n,m)dndmpdμ(pd) 考虑维护最后一项的前缀和。线性筛求莫比乌斯函数,然后枚举素数 O ( n log ⁡ n ) O(\frac{n}{\log n}) O(lognn),更新其倍数,均摊 O ( log ⁡ n ) O(\log n) O(logn)。总时间复杂度 O ( n + T n ) O(n+T\sqrt n) O(n+Tn )

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
using namespace std;
#define maxn 10000005
typedef long long ll;
int prime[maxn >> 1], mu[maxn], _sum[maxn], sum[maxn];
bool used[maxn];

int sieve()
{
    int p = 0;
    mu[1] = 1;
    for (int i = 2; i < maxn; ++i)
    {
        if (!used[i])
        {
            prime[p++] = i;
            mu[i] = -1;
        }
        for (int j = 0; j < p && i * prime[j] < maxn; ++j)
        {
            int k = i * prime[j];
            used[k] = 1;
            if (i % prime[j] == 0)
            {
                mu[k] = 0;
                break;
            }
            else
                mu[k] = -mu[i];
        }
    }
    return p;
}

void init()
{
    int p = sieve();
    for (int i = 0; i < p; ++i)
        for (int j = prime[i], k = 1; j < maxn; j += prime[i], ++k)
            _sum[j] += mu[k];
    for (int i = 1; i < maxn; ++i)
        sum[i] = sum[i - 1] + _sum[i];
}

ll count(int n, int m)
{
    ll res = 0;
    if (n > m)
        swap(n, m);
    for (int i = 1, last; i <= n; i = last + 1)
    {
        last = min(n / (n / i), m / (m / i));
        res += 1LL * (n / i) * (m / i) * (sum[last] - sum[i - 1]);
    }
    return res;
}

int main()
{
    init();
    int t, n, m;
    scanf("%d", &t);
    while (t--)
    {
        scanf("%d%d", &n, &m);
        printf("%lld\n", count(n, m));
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值