1 条题解

  • 0
    @ 2025-8-24 22:10:43

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar moongazer
    We do not know and will not know

    搬运于2025-08-24 22:10:43,当前版本为作者最后更新于2020-01-24 00:35:24,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    一种O(O(值域))时间预处理O(1)O(1)时间求最大公约数(gcd\gcd)的算法

    $\mathfrak{View\space it\space on\space my\space Blog}$

    Update in 2020.08.07:修正了一处错误,感谢@Kinesis的指正

    一些约定

    1. NN为询问的值域
    2. PrimePrime为全体素数集合
    3. 集合{a1,a2,,am}\{a_1,a_2,\cdots,a_m\}nn的分解,当且仅当a1×a2××am=na_1\times a_2\times\cdots\times a_m=n

    原理

    定理一

    内容

    可以将值域中的每个xx分解成{a,b,c}\{a,b,c\},满足a,b,cxa,b,c\leq\sqrt{x}Prime\in Prime(定义这种分解为合法分解)

    证明

    不妨设abca\leq b\leq ccPrimec\notin Primec>xc>\sqrt{x},则cc可分解为{d,e}\{d,e\}ded\leq edxd\leq\sqrt{x},而a×b=xc<xa\times b=\frac{x}{c}<\sqrt{x}则有nn的分解{d,a×b,e}\{d,a\times b,e\},若e>xe>\sqrt{x}则可按该规律一直分解直到ePrimee\in Primex\leq\sqrt{x}

    定理二

    内容

    对于询问gcd(x,y)\gcd(x,y),分别考虑a,b,ca,b,c对答案的贡献,aa对答案的贡献为gcd(a,y)\gcd(a,y),再将yy除以gcd(a,y)\gcd(a,y)(这个因子已经被算过,不能重复计算),再对b,cb,c干同样的事,三个贡献相乘即为gcd(x,y)\gcd(x,y)

    证明

    易得引理若rp,rqr\mid p, r\mid qgcd(p,q)=r×gcd(pr,qr)\gcd(p,q)=r\times\gcd(\frac{p}{r},\frac{q}{r})

    分别代入$\left\{ \begin{aligned} &p_1=a\times b\times c,q_1=y,r_1=\gcd(a,q_1) \\ &p_2=b\times c,q_2=\frac{q_1}{r_1},r_2=\gcd(b,q_2) \\ &p_3=c,q_3=\frac{q_2}{r_2},r_3=\gcd(c,q_3) \end{aligned} \right. $即可

    实现

    我们发现实现的难点在于如何在O(N)O(N)时间内进行分解,询问部分较为容易

    分解

    对于x2x\geq2,找到xx的最小质因子pp以及xp\frac{x}{p}的合法分解{a0,b0,c0}\{a_0,b_0,c_0\}a0b0c0a_0\leq b_0\leq c_0,xx的一种合法分解即为{a0×p,b0,c0}\{a_0\times p,b_0,c_0\}的升序排序

    考虑证明:

    1. xPrimex\in Prime时显然成立,分解为{1,1,x}\{1,1,x\}
    2. px4p\le\sqrt[4]{x}时将a0xp3a_0\leq\sqrt[3]{\frac{x}{p}}带入有a0×pxa_0\times p\le\sqrt{x}
    3. 考虑p>x4p>\sqrt[4]{x}的情况,(1.)\left(1.\right) a0=1a_0=1,显然有a0×p=pxa_0\times p=p\le\sqrt{x}(2.)\left(2.\right) a1a\neq1,由于xx不是素数,xp\frac{x}{p}的最小质因子qq即为xx的第二小质因子,一定p\geq p,而a0,b0,c0a_0,b_0,c_0都为xp\frac{x}{p}的非11因子,有pqa0b0c0p\leq q\leq a_0\leq b_0\leq c_0,p×a0×b0×c0>(x4)4=xp\times a_0\times b_0\times c_0>(\sqrt[4]{x})^4=xp×a0×b0×c0=xp\times a_0\times b_0\times c_0=x相矛盾,故不存在此情况

    所以只用跑一次线性筛,用最小质因子更新即可,然后预处理出n×n\sqrt{n}\times\sqrt{n}gcd\gcd数组

    代码如下

    // fac为合法分解,isp表示是否非质数,pri为质数数组,tot为pri的大小,pre为预处理的gcd数组,M为值域,T为sqrt(M)
    void work() {
      fac[1][0] = fac[1][1] = fac[1][2] = 1;
      for (int i = 2; i <= M; ++i) {
        if (!isp[i]) {
          fac[i][0] = fac[i][1] = 1;
          fac[i][2] = i;
          pri[++tot] = i;
        }
        for (int j = 1; pri[j] * i <= M; ++j) {
          int tmp = pri[j] * i;
          isp[tmp] = true;
          fac[tmp][0] = fac[i][0] * pri[j];
          fac[tmp][1] = fac[i][1];
          fac[tmp][2] = fac[i][2];
          if (fac[tmp][0] > fac[tmp][1]) {
            fac[tmp][0] ^= fac[tmp][1] ^= fac[tmp][0] ^= fac[tmp][1];
          }
          if (fac[tmp][1] > fac[tmp][2]) {
            fac[tmp][1] ^= fac[tmp][2] ^= fac[tmp][1] ^= fac[tmp][2];
          }
    // 对于整数运算a ^= b ^= a ^= b等价于swap(a, b)这里就是手动进行length = 3的排序
          if (i % pri[j] == 0) {
            break;
          }
        }
      }
      for (int i = 0; i <= T; ++i) {
        pre[0][i] = pre[i][0] = i;
      }
      for (int i = 1; i <= T; ++i) {
        for (int j = 1; j <= i; ++j) {
          pre[i][j] = pre[j][i] = pre[j][i % j];
        }
      }
    }
    

    查询

    若当前枚举的为aa,只需注意a>Na>\sqrt{N}时分aya\mid yaya\nmid y两种情况即可

    以下为代码

    int gcd(int a, int b) {
    // 不想写if-else所以用三目运算符代替并缩进了一下
      int ans = 1;
      for (int i = 0; i < 3; ++i) {
        int tmp = (fac[a][i] > T) ?
                    (b % fac[a][i] ?
                       1
                     : fac[a][i]
                    )
                  : pre[fac[a][i]][b % fac[a][i]];
        b /= tmp;
        ans *= tmp;
      }
      return ans;
    }
    

    本题做法

    基本上已经没了,就是求gcd\gcd然后按题目给的算,放个代码吧

    const int N = 5000;
    const int M = 1000000;
    const int T = 1000;
    const int Mod = 998244353;
    
    void work();
    int gcd(int, int);
    
    int pre[T + 2][T + 2];
    int a[N + 2], b[N + 2];
    int fac[M + 2][3];
    bool isp[M + 2];
    int pri[M / 10], tot;
    int n;
    
    int main () {
      work();
      read(n);
      for (int i = 1; i <= n; ++i) {
        read(a[i]);
      }
      for (int i = 1; i <= n; ++i) {
        read(b[i]);
      }
      for (int i = 1; i <= n; ++i) {
        int ans = 0;
        for (int j = 1, now = i; j <= n; ++j, now = now * 1ll * i % Mod) {
          ans = (ans + int(now * 1ll * gcd(a[i], b[j]) % Mod)) % Mod;
        }
        write(ans), EL;
      }
      return 0;
    }
    
    void work() {
      fac[1][0] = fac[1][1] = fac[1][2] = 1;
      for (int i = 2; i <= M; ++i) {
        if (!isp[i]) {
          fac[i][0] = fac[i][1] = 1;
          fac[i][2] = i;
          pri[++tot] = i;
        }
        for (int j = 1; pri[j] * i <= M; ++j) {
          int tmp = pri[j] * i;
          isp[tmp] = true;
          fac[tmp][0] = fac[i][0] * pri[j];
          fac[tmp][1] = fac[i][1];
          fac[tmp][2] = fac[i][2];
          if (fac[tmp][0] > fac[tmp][1]) {
            fac[tmp][0] ^= fac[tmp][1] ^= fac[tmp][0] ^= fac[tmp][1];
          }
          if (fac[tmp][1] > fac[tmp][2]) {
            fac[tmp][1] ^= fac[tmp][2] ^= fac[tmp][1] ^= fac[tmp][2];
          }
          if (i % pri[j] == 0) {
            break;
          }
        }
      }
      for (int i = 0; i <= T; ++i) {
        pre[0][i] = pre[i][0] = i;
      }
      for (int i = 1; i <= T; ++i) {
        for (int j = 1; j <= i; ++j) {
          pre[i][j] = pre[j][i] = pre[j][i % j];
        }
      }
    }
    int gcd(int a, int b) {
      int ans = 1;
      for (int i = 0; i < 3; ++i) {
        int tmp = (fac[a][i] > T) ?
                    (b % fac[a][i] ?
                       1
                     : fac[a][i]
                    )
                  : pre[fac[a][i]][b % fac[a][i]];
        b /= tmp;
        ans *= tmp;
      }
      return ans;
    }
    
    • 1

    信息

    ID
    4411
    时间
    1200ms
    内存
    32MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者