• 【BZOJ 2820】YY的GCD


    Problem

    Description

    给定(N), (M),求(1le xle N), (1le yle M)(gcd(x,y)) 为质数的 ((x,y)) 有多少对

    多组数据

    Input Format

    第一行一个整数 (T) 表述数据组数

    接下来 (T) 行,每行两个正整数,表示 (N,M)

    Output Format

    (T) 行,每行一个整数表示第 (i) 组数据的结果

    Sample

    Input

    2
    10 10
    100 100
    

    Output

    30
    2791
    

    Range

    (T=10000)

    (N,Mle 10000000)

    Algorithm

    莫比乌斯反演

    Mentality

    莫比乌斯反演入门题。

    所谓莫比乌斯反演题,其实都是数学公式的盛宴 = = 。

    题目要我们求的,显然就是这个:

    [sum_{i=1}^n sum_{j=1}^m [gcd(i,j)=prime] ]

    首先令 (nle m) ,那么我们可以开始变化了:

    [sum_{pin prime}sum_{i=1}^nsum_{j=1}^m[gcd(i,j)=p] ]

    由于 (gcd(i,j)==p) ,所以 (i|p&&j|p) ,那我们可以改为枚举 (i×p,j×p) ,则式子变为:

    [sum_{pin prime}sum_{i=1}^{lfloor frac{n}{p} floor}sum_{j=1}^{lfloor frac{m}{p} floor}[gcd(i,j)=1] ]

    接着,我们知道莫比乌斯函数 (mu) 有这样一个性质:

    [sum_{d|n}mu(d) =[n=1] ]

    那么我们用这个东西去代 ([gcd(i,j)=1]) ,我们就可以得到这样一个式子:

    [sum_{pin prime}sum_{i=1}^{lfloor frac{n}{p} floor}sum_{j=1}^{lfloor frac{m}{p} floor}sum_{d|gcd(i,j)}mu(d) ]

    这里我们考虑摆脱 (i,j) 的束缚,改为枚举那些在后面出现过的每个 (mu (d)) ,统计它们做出的贡献。那么式子画风一变,就会这样:

    [sum_{pin prime}sum_{d=1}^nmu(d)sum_{i=1}^{lfloor frac{n}{p} floor}sum_{j=1}^{lfloor frac{m}{p} floor}[d|i&&d|j] ]

    不难发现,或者说一目了然地发现这样一件事:

    [sum_{i=1}^{lfloor frac{n}{p} floor}sum_{j=1}^{lfloor frac{m}{p} floor}[d|i&&d|j]=lfloorfrac{n}{pd} floor*lfloorfrac{m}{pd} floor ]

    式子进一步变得简洁明了了:

    [sum_{pin prime}sum_{d=1}^nmu(d)*lfloorfrac{n}{pd} floor*lfloorfrac{m}{pd} floor ]

    不过这样子的式子求起来还不是行,我们要进一步快乐化简,设 (T=pd) ,那么我们的式子变得如下了:

    [sum_{T=1}^nsum_{p=1,pin prime,p|T}^nmu(frac{T}{p})*lfloorfrac{n}{T} floor*lfloorfrac{m}{T} floor ]

    由于对于同一个 (T)(lfloorfrac{n}{T} floor*lfloorfrac{m}{T} floor) 的值并不会发生改变,那么我们完全可以提到 (sum) 外面,接下来式子变成了:

    [sum_{T=1}^nlfloorfrac{n}{T} floor*lfloorfrac{m}{T} floorsum_{p=1,pin prime,p|T}^nmu(frac{T}{p}) ]

    那么答案就会变得非常显然了,设 (f(T)=sum_{p=1,pin prime,p|T}^nmu(frac{T}{p})) ,我们只需要求出 (f(T)) 们就行了。

    不过这里还要用到一种叫做 整除分块 的东西,因为我们发现,对于某一些连续的数 (T) ,它们的 (lfloorfrac{n}{T} floor*lfloorfrac{m}{T} floor) 完全是一样的,我们只需要通过前缀和计算这一段数对应的 (f(T)) 的和,统一乘上 (lfloorfrac{n}{T} floor*lfloorfrac{m}{T} floor) 即可。

    怎么计算这个玩意呢?当然是利用这样一个结论:对于 (isim lfloorfrac{n}{lfloorfrac{n}{i} floor} floor) 而言,它们的 (lfloorfrac{n}{i} floor) 的值都是相同的。那么我们的整除结果就被分成了一段一段相同的数,是谓所谓整除分块 = = 。具体证明很简单,可以看一下这位 Dalao 的 Blog

    完毕。

    Code

    #include <cstdio>
    #include <iostream>
    using namespace std;
    int T, n, m, cnt, pri[10000001], mu[10000001], sum[10000001];
    bool vis[10000001];
    void init() {
      mu[1] = 1;
      for (int i = 2; i <= 10000000; i++) {
        if (!vis[i]) pri[++cnt] = i, mu[i] = -1;
        for (int j = 1; j <= cnt && pri[j] * i <= 10000000; j++) {
          vis[pri[j] * i] = true;
          if (!(i % pri[j])) break;
          mu[pri[j] * i] = -mu[i];
        }
      }  //求出莫比乌斯函数
      for (int i = 1; i <= cnt; i++)
        for (int j = 1; j * pri[i] <= 10000000; j++)
          sum[j * pri[i]] += mu[j];                              //统计 f 函数
      for (int i = 1; i <= 10000000; i++) sum[i] += sum[i - 1];  //统计前缀和
    }
    int main() {
      freopen("2257.in", "r", stdin);
      freopen("2257.out", "w", stdout);
      init();  //预处理前缀和啥的
      cin >> T;
      while (T--) {
        long long ans = 0;
        scanf("%d%d", &n, &m);
        if (n > m) swap(n, m);
        for (int i = 1, r; i <= n; i = r + 1) {
          r = min(n / (n / i), m / (m / i));  //得到上界
          ans += 1ll * (sum[r] - sum[i - 1]) * (n / i) * (m / i);
        }
        printf("%lld
    ", ans);
      }
    }
    
    
  • 相关阅读:
    修改 MySQL 的 sql_mode 模式方法
    PHP 实现 Redis 连接池
    【转载】php解决高并发问题
    PHP 7 不适用函数:password_hash
    PDO 防止 SQL 注入示例
    记录一次 header 参数格式引发的错误
    Laravel 框架数据库查询构造器中 when 的易犯错误
    PHP 7.3.4 安装 Redis 4.0(Windows系统)
    汇编语言全梳理(精简版)
    Anaconda安装和使用
  • 原文地址:https://www.cnblogs.com/luoshuitianyi/p/10478729.html
Copyright © 2020-2023  润新知