POJ 1845 Sumdiv (唯一分解定理+求等比数列前n项和)

本文介绍一种高效算法来解决大数幂次的因数之和问题,并给出具体实现代码。通过质因数分解及等比数列求和原理,结合模运算公式,实现了对大数A^B的因数之和求模运算。

题目大意:先求出A^B(0 <= A,B <= 50000000)的因数之和sum, 然后求sum % 9901的值。

由于A,B的取值范围很大, 所以直接运算肯定不行。

唯一分解定理

  • 任何一个大于1的自然数 N,如果N不为质数,那么N可以唯一分解成有限个质数的乘积

所以 A = p1^q1 * p2^q2 * …… * pn^qn(pi为素数)
从而 A^B = (p1^q1 * p2^q2 * …… * pn^qn) ^ B
= p1^(q1*B) * p2^(q2*B) * …… * pn^(qn*B)
由于pi是素数, 很容易推得pi^(qi*B)的因数之和为

    1 + pi + pi^2 + ...... + pi^(qi*B)

从而A^B的因数之和为

(1 + p1 + p1^2 + ...... + p1^(q1*B)) * (1 + p2 + p2^2 + ...... + p2^(q2*B)) * ...... * (1 + pn + pn^2 + ...... + pn^(qn*B))

如果先求出这整个的和值 , 肯定超long long,所以要边乘边取模。

模运算公式

  • (a + b) % p = (a % p + b % p) % p
  • (a - b) % p = (a % p - b % p) % p
  • (a * b) % p = (a % p * b % p) % p
  • (a ^ b) % p = ((a % p)^b) % p

另外由于qi * B的值很大, 一步步算肯定会超时, 注意到每一项都是一个等比数列的和。

求等比数列前N项和

求表达式等比数列的值

  1. 这里写图片描述时 , 这里写图片描述
  2. 当时 , 则有
    这里写图片描述
  3. 当时, 有
    这里写图片描述

由以上分析 , 用分治法求等比数列前N项和。
参考:http://blog.youkuaiyun.com/ACdreamers/article/details/7851144

代码

#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstring>

using namespace std;
typedef long long ll;
const int maxn = 10000;
ll p[maxn] , q[maxn];

ll pow_mod(ll a , ll n , ll m)
{
    if(n == 0) return 1;
    ll x = pow_mod(a , n >> 1 , m);
    ll ret = x * x % m;
    if(n & 1) ret = ret * a % m;
    return ret;
}

//求(1 + a + a^2 + ... + a^n) % m
ll sum(ll a , ll n , ll m)
{
    if(!n) return 1;
    if(n == 1) return a % m;
    ll ret = 0;
    if(n&1) {
        ret = (1 + pow_mod(a , (n - 1) / 2 + 1 , m)) % m;
        ret = (ret * sum(a , (n - 1) / 2 , m)) % m;
        ret = (ret + pow_mod(a , (n - 1) / 2 + 1 , m)) % m;
    }
    else {
        ret = (1 + pow_mod(a , n / 2 , m)) % m;
        ret = (ret * sum(a , n / 2 , m)) % m;
    }
    return ret;
}
int main()
{
    int a , b;
    while(cin >> a >> b)
    {
        if(!a) {cout << 0 << endl; continue;}
        if(!b) {cout << 1 << endl; continue;}
        int n = 0;
        // 根号法 + 递归法 分解整数A
        for(int i = 2; i * i <= a && a != 1; i++) if(a % i == 0)   
        {
            p[n] = i , q[n] = 0;
            while(a % i == 0) {q[n]++; a /= i;}
            n++;
            if(i != 2) i++; //奇偶法
        }
        if(a != 1) {p[n] = a; q[n++]=1;}

        ll ans = 1 , mod = 9901;
        for(int i = 0; i < n; i++) {
            ll tmp = (sum(p[i] , q[i] * b , mod) + 1) % mod;
            ans = ans * tmp % mod;
        }
        cout << ans << endl;
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值