Sumdiv(快速幂 + 约数和)

博客主要介绍了POJ 1845 - Sumdiv这道数学题目,涉及质因数分解和等比数列求和的知识。通过唯一分解定理,将给定数表示为质因数的乘积,然后利用等比数列的求和公式,分别计算每个质因子不同次方的和,并累乘得到所有约数的和。代码部分使用C++实现,包括快速幂优化的等比数列求和函数。
摘要由CSDN通过智能技术生成

Sumdiv

[Link](1845 – Sumdiv (poj.org))

题意

给你a,b,问你 a b a^b ab的所有的约数之和模9901之后是多少。

题解

首先根据唯一分解定理:任何一个自然数都可以被拆成一系列质数因子的乘积,就是 a b = p 1 a 1 ∗ p 2 a 2 . . . ∗ p n a n a^b=p_1^{a_1}*p_2^{a_2}...*p_n^{a_n} ab=p1a1p2a2...pnan a b a^b ab的每一个质因子有 [ 0 , a i ] [0, a_i] [0,ai]种取法,每一个质因子的每一种取法的组合,构成了不同的约数,所以约数之和就是所有取法的值的和也就是 ( 1 + p 1 + p 1 2 + . . . + p 1 a 1 ) ∗ ( 1 + p 2 + p 2 2 + . . . + p 2 a 2 ) ∗ . . . ∗ ( 1 + p n + p n 2 + . . . + p n a n ) (1+p_1+p_1^2+...+p_1^{a_1})*(1+p_2+p_2^2+...+p_2^{a_2})*...*(1+p_n+p_n^2+...+p_n^{a_n}) (1+p1+p12+...+p1a1)(1+p2+p22+...+p2a2)...(1+pn+pn2+...+pnan)。考虑先把 a b a^b ab中所有的质数和它的次数筛出来,然后分别求出上面每一个等比数列,累乘即可。对于求等比数列我们发现他是具有递推关系的,可以分开来看:

  1. n是奇数 S n = ( 1 + p + p 2 + . . + p n / 2 ) ∗ ( 1 + p n / 2 + 1 ) Sn = (1+p+p^2+..+p^{n/2})*(1+p^{n/2+1}) Sn=(1+p+p2+..+pn/2)(1+pn/2+1)

  2. n是偶数 S n = ( 1 + p + p 2 + . . + p n / 2 − 1 ) ∗ ( 1 + p n / 2 + 1 ) + p n / 2 Sn=(1+p+p2+..+p^{n/2 - 1})*(1+p^{n/2+1})+p^{n/2} Sn=(1+p+p2+..+pn/21)(1+pn/2+1)+pn/2

    直接递归求一下即可,求 p a i p^{a_i} pai的时候用快速幂即可。

    Code

    #include <stdio.h>
    #include<math.h> 
    #include<stdlib.h>
    #include<cstring>
    #include <iostream>
    #include<vector>
    #include<map>
    #define x first
    #define y second
    using namespace std;
    typedef long double ld;
    typedef long long LL;
    typedef pair<int, int> PII;
    typedef pair<double, double> PDD;
    typedef unsigned long long ULL;
    const int N = 1e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 9901;
    const double eps = 1e-8;
    int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
    int h[N], e[M], ne[M], w[M], idx;
    void add(int a, int b, int v = 0) {
        e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
    }
    LL n, m, k;
    int a[N];
    LL qmi(LL a, LL b, LL mod) {
        LL res = 1;
        while (b) {
            if (b & 1) res = res * a % mod;
            a = a * a % mod;
            b >>= 1;
        }
        return res;
    }
    LL infact(LL p, LL n) {
        if (!n) return 1;
        if (n & 1) return infact(p, n / 2) * (1 + qmi(p, n / 2 + 1, mod)) % mod;
        else    return (infact(p, n / 2 - 1) * (1 + qmi(p, n / 2 + 1, mod)) + qmi(p, n / 2, mod)) % mod; 
    }
    int main() {
        ios::sync_with_stdio(false), cin.tie(0);
        cin >> n >> m;
      //  unordered_map<int, int> primes;
        vector<LL> primes, cnts;
        if (!n) cout << 0 << endl;
        else if (n == 1 || !m) cout << 1 << endl;
        else {
            for (LL i = 2; i <= n / i; i ++ ) {
                if (n % i == 0) {
                    primes.push_back(i);
                    int cnt = 0;
                    while (n % i == 0) cnt ++,  n /= i;
                    cnts.push_back(cnt);
                }
            }
            if (n > 1) primes.push_back(n), cnts.push_back(1);
            LL res = 1;
            for (int i = 0; i < primes.size(); i ++ ) 
                res = res * infact(primes[i], cnts[i] * m) % mod;
            cout << res << endl;
        } 
        return 0;
    }
    
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是用C++实现上述思路的代码: ```cpp #include <iostream> #include <vector> using namespace std; const int mod = 1000000007; // 线性筛法计算欧拉函数的前缀和 void calculatePhi(vector<int>& phi, vector<int>& prime, vector<bool>& isPrime, int k) { phi[1] = 1; for (int i = 2; i <= k; i++) { if (isPrime[i]) { prime.push_back(i); phi[i] = i - 1; } for (int j = 0; j < prime.size() && i * prime[j] <= k; j++) { isPrime[i * prime[j]] = false; if (i % prime[j] == 0) { phi[i * prime[j]] = phi[i] * prime[j]; break; } else { phi[i * prime[j]] = phi[i] * (prime[j] - 1); } } } // 计算前缀和 for (int i = 2; i <= k; i++) { phi[i] = (phi[i] + phi[i - 1]) % mod; } } // 计算⌊n/i⌋的前缀和 vector<int> calculateDivPrefixSum(int n, int k) { vector<int> sumDiv(k + 1, 0); for (int i = 1; i <= k; i++) { sumDiv[i] = (sumDiv[i - 1] + n / i) % mod; } return sumDiv; } int main() { int k; cin >> k; vector<int> phi(k + 1, 0); vector<int> prime; vector<bool> isPrime(k + 1, true); calculatePhi(phi, prime, isPrime, k); vector<int> sumDiv = calculateDivPrefixSum(k, k); int result = 0; for (int i = 1; i <= k; i++) { int factorCount = k / i; int temp = (sumDiv[factorCount] - sumDiv[i - 1] + mod) % mod; result = (result + phi[i] * temp) % mod; } cout << result << endl; return 0; } ``` 你可以将上述代码保存为一个.cpp文件,然后使用C++编译器进行编译和运行。输入k的值,即可得到最终结果。 希望对你有帮助!如果还有其他问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值