洛谷2257:YY的GCD

时间:2020-01-09
本文章向大家介绍洛谷2257:YY的GCD,主要包括洛谷2257:YY的GCD使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

洛谷2257:YY的GCD

题意描述

  • \(\sum_{i=1}^N\sum_{j=1}^M[gcd(i,j)==p]\),其中\(p\)是所有质数。
  • 数据范围\(N,M\leq 10^7\),有\(10000\)组测试数据。

思路:

  • 莫比乌斯反演
  • \(f(d)\)\(gcd(i,j)==d\)的个数:\(f(d)=\sum_{i=1}^n\sum_{j=1}^m[gcd(i,j)==d]\)
  • \(F(n)\)\(gcd(i,j)==kn\)的个数,其中\(k=1,2,...\)
  • \(F(n)=\sum_{n|d}f(d)=\frac{N}{n}\frac{M}{n}\)
  • \(f(n)=\sum_{n|d}\mu(\frac{d}{n})F(d)\)

  • \(ans=\sum_{p\in primes}\sum_{i=1}^N\sum_{j=1}^M[gcd(i,j)==p]\).

  • \(=\sum_{p\in primes}f(p)\)

  • \(=\sum_{p\in primes}\sum_{p|d}\mu(\frac{d}{p})F(d)\).

  • 枚举\(\frac{d}{p}=t\),有

  • \(=\sum_{p\in primes}\sum_{t=1}^{min\{\frac{N}{p},\frac{M}{p}\}}\mu(t)\frac{N}{tp}\frac{M}{tp}\)

  • \(T=tp\),有:

  • \(\sum_{p\in primes}\sum_{t=1}^{min\{\frac{N}{p},\frac{M}{p}\}}\mu(t)*\frac{N}{T}\frac{M}{T}\)

  • 枚举一下\(T\),有:

  • \(\sum_{T=1}^{min\{N,M\}}\frac{N}{T}\frac{M}{T}\sum_{k|T,k\in primes}\mu(\frac{T}{k})\)

  • 后面这一项求和可以预处理前缀和\(O(1)\)查询。

  • 前面一项可以通过整除分块优化至\(O(\sqrt{n})\)

  • #include<bits/stdc++.h>
    using namespace std;
    const int maxn = 1e7 + 1e5;
    int mu[maxn], primes[maxn], cnt;
    long long sum[maxn], f[maxn];
    bool vis[maxn];
    void get_mu(int n)
    {
        mu[1] = 1;
        for(int i = 2; i <= n; i++)
        {
            if(!vis[i])
            {
                primes[++cnt] = i;
                mu[i] = -1;
            }
            for(int j = 1; primes[j] <= n/i; j++)
            {
                vis[primes[j]*i] = 1;
                if(i % primes[j] == 0) break;
                else mu[i*primes[j]] = -mu[i];
            }
        }
        for(int i = 1; i <= cnt; i++)
            for(int j = 1; primes[i] <= n/j; j++)
                f[j*primes[i]] += mu[j];
        for(int i = 1; i <= n; i++)
            sum[i] = sum[i-1] + f[i];
    }
    
    int main()
    {
        get_mu(10000000+5);
        int T; scanf("%d", &T);
        while(T--)
        {
            int n, m;
            long long ans = 0;
            scanf("%d%d", &n, &m);
            if(n > m) swap(n, m);
            for(int l = 1, r; l <= n; l = r+1)
            {
                r = min(n/(n/l), m/(m/l));
                ans += 1ll*(1ll*(n/l)*1ll*(m/l) * (sum[r]-sum[l-1]));
            }
            printf("%lld\n", ans);
        }
        return 0;
    }

原文地址:https://www.cnblogs.com/zxytxdy/p/12170596.html