• AcWing 97. 约数之和


    题目传送门

    一、理论知识

    算术基本定理

    \[\large N=p_1^{\alpha_1}\cdot p_2^{\alpha_2}\cdot ... \cdot p_k^{\alpha_k} \]

    约数个数定理

    \[\large f(N)=\prod_{i=1}^{k}(a_i+1)= (\alpha_1+1)(\alpha_2+1)...(\alpha_k+1) \]

    证明:因为\(\large p_1^{\alpha_1}\)的约数有\(\large p_1^0,p_1^1,p_1^2,...,p_1^{\alpha_1}\),共\(\large a_1+1\)个,同理\(p_k^{\alpha_k}\)的约数有\(\large a_k+1\)个,根据乘法原理,就知道了约数的总个数。

    约数和定理

    \[\large Sum(A)=(1+p_1^1+p_1^2+...+p_1^{\alpha_1})(1+p_2^1+p_2^2+...+p_2^{\alpha_2})...(1+p_n^1+p_n^2+...+p_n^{\alpha_n}) \]

    简写

    \[\large Sum(A)=\prod_{i=1}^{n}(\sum_{j=0}^{\alpha_i}p_i^j) \]


    前置练习题

    约数之和

    分解质因数


    举栗子: \(180=2^2∗3^2∗5^1\)

    约数个数:\((2+1)(2+1)(1+1)=18\)

    约数和:\((1+2+4)(1+3+9)(1+5)=546\)

    回到题目,在这个题目里的 约数和 就是如下式子:

    \[\large Ans=\prod_{i=1}^{n}(\sum_{j=0}^{\alpha_i*B}p_i^j) \]


    二、分治法

    分治法计算等比数列求和

    \(\large sum(p, k)=p^0+p^1+…+p^{k−1}\)

    • \(k\)为偶数时

    \[\large sum(p,k)=p^0+p^1+…+p^{k/2−1}+p^{k/2}+p^{k/2+1}+…+p^{k−1} \\ \Rightarrow \\ (p^0+p^1+…+p^{k/2−1})+p^{k/2}∗(p^0+p^1+…+p^{k/2−1}) \\ \Rightarrow \\ sum(p,k/2)+p^{k/2}∗sum(p,k/2) \\ \Rightarrow \\ p^{k/2+1}∗sum(p,k/2) \]

    • \(k\)为奇数时
      为了更方便调用我们写的偶数项情况,可以单独拿出最后一项,把剩下的项转化为求偶数项的情况来考虑,再加上最后一项,就是奇数项的情况了。也即\(sum(p,k−1)+p^{k−1}\)
    //sum(p,k)=p^0 + p^1 .. + p^{k-1}
    int sum(int p, int k) {
        if(k == 1) return 1;  //边界
        if(k % 2 == 0)  
            return (LL)(qmi(p, k / 2) + 1) * sum(p, k / 2) % mod;
        return (qmi(p, k - 1) + sum(p, k - 1)) % mod;
    }
    

    完整代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    
    const int mod = 9901;
    int A, B;
    
    //分解质因数
    map<int, int> primes; // map存的是数对,key+value,默认按key由小到大排序
    void divide(int x) {
        for (int i = 2; i <= x / i; i++)
            while (x % i == 0) primes[i]++, x /= i;
        if (x > 1) primes[x]++;
    }
    
    //快速幂
    int qmi(int a, int b) {
        int res = 1;
        while (b) {
            if (b & 1) res = (LL)res * a % mod;
            a = (LL)a * a % mod;
            b >>= 1;
        }
        return res;
    }
    
    // p0 + .. + pk-1
    int sum(int p, int k) {
        if (k == 1) return 1; //边界
        if (k % 2 == 0)
            return (LL)(qmi(p, k / 2) + 1) * sum(p, k / 2) % mod;
        return (qmi(p, k - 1) + sum(p, k - 1)) % mod;
    }
    
    int main() {
        scanf("%d %d", &A, &B);
        //对A分解质因子
        divide(A);
    
        int res = 1;
        for (auto it : primes) {
            // p是质因子,k是质因子的次数
            int p = it.first, k = it.second * B; //约数和公式,需要到每个质因子的it.second次方*B
    
            // res要乘上每一项, 注意这里是k + 1
            res = (LL)res * sum(p, k + 1) % mod;
        }
        if (!A) res = 0; //还要特判A是不是0
        printf("%d\n", res);
        return 0;
    }
    

    时间复杂度 \(O(\sqrt{n}lognlogn)\)


    三、公式法

    等比数列求和

    \[\large p^0+p^1+p^2+...+p^{k-1}=\frac{p^{k}-1}{p-1} \]

    除了分治法外,还可以用乘等比数列和公式得出分子,再乘分母逆元的做法:

    快速幂求逆元
    这样的话,就可以愉快地套用高中学的等比数列和公式来求每一项,但这里需要特判逆元不存在的情况:

    • 分母\(p-1\)\(mod\)的倍数,不存在逆元,这时直接乘\((1+p^1+…p^k)\% mod\), 即 \(k+1\)

    • 分母\(p-1\)不是\(mod\)的倍数,存在逆元,这是需要用快速幂求分子,再用快速幂求分母的逆元,两者相乘就得到了每一项

    实现代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    
    const int mod = 9901;
    int A, B;
    
    //分解质因数
    map<int, int> primes; // map存的是数对,key+value,默认按key由小到大排序
    void divide(int x) {
        for (int i = 2; i <= x / i; i++)
            while (x % i == 0) primes[i]++, x /= i;
        if (x > 1) primes[x]++;
    }
    
    //快速幂
    int qmi(int a, int b) {
        int res = 1;
        while (b) {
            if (b & 1) res = (LL)res * a % mod;
            a = (LL)a * a % mod;
            b >>= 1;
        }
        return res;
    }
    
    int main() {
        scanf("%d %d", &A, &B);
        //对A分解质因子
        divide(A);
    
        int res = 1;
        for (auto it : primes) {
            // p是质因子,k是质因子的次数
            int p = it.first, k = it.second * B;
            // res要乘上每一项, 注意这里是k + 1
            if ((p - 1) % mod == 0) {
                //不存在逆元,由于p-1的是mod的倍数, 故p%mod=1
                //所以1 + p + ... + p^k每个数%mod都是1,共k + 1个数,总就是k + 1
                res = (LL)res * (k + 1) % mod;
            } else
                //分子用快速幂计算,注意标准公式和此题的区别,k+1
                //分母用费马小定理求逆元 qmi(p-1,mod-2)
                res = (LL)res * (qmi(p, k + 1) - 1) % mod * qmi(p - 1, mod - 2) % mod;
        }
        if (!A) res = 0;
        printf("%d\n", (res % mod + mod) % mod);
        return 0;
    }
    

    时间复杂度\(O(\sqrt{n}logn)\)


  • 相关阅读:
    Lucene in action 笔记 case study
    关于Restful Web Service的一些理解
    Lucene in action 笔记 analysis篇
    Lucene in action 笔记 index篇
    Lucene in action 笔记 term vector
    Lucene in action 笔记 search篇
    博客园开博记录
    数论(算法概述)
    DIV, IFRAME, Select, Span标签入门
    记一个较困难的SharePoint性能问题的分析和解决
  • 原文地址:https://www.cnblogs.com/littlehb/p/16411168.html
Copyright © 2020-2023  润新知