您的位置:首页 > 其它

【POJ 1845】 Sumdiv (整数唯分+约数和公式+二分等比数列前n项和+同余)

2015-08-21 22:17 337 查看
【POJ 1845】 Sumdiv

用的东西挺全 最主要通过这个题学了约数和公式跟二分求等比数列前n项和 还有一种小优化的整数拆分

整数的唯一分解定理:

任意正整数都有且只有一种方式写出其素因子的乘积表达式。

A=(p1^k1)*(p2^k2)*(p3^k3)*....*(pn^kn) 其中pi均为素数
约数和公式:

对于已经分解的整数A=(p1^k1)*(p2^k2)*(p3^k3)*....*(pn^kn)

有A的所有因子之和为

S = (1+p1+p1^2+p1^3+...p1^k1) * (1+p2+p2^2+p2^3+….p2^k2) * (1+p3+ p3^3+…+ p3^k3) * .... * (1+pn+pn^2+pn^3+...pn^kn)

用递归二分求等比数列1+pi+pi^2+pi^3+...+pi^n:

(1)若n为奇数,一共有偶数项,则:

1 + p + p^2 + p^3 +...+ p^n

= (1+p^(n/2+1)) + p * (1+p^(n/2+1)) +...+ p^(n/2) * (1+p^(n/2+1))

=(1 + p + p^2 +...+ p^(n/2)) * (1 + p^(n/2+1))
->奇数时两边平分

上式红色加粗的前半部分恰好就是原式的一半,那么只需要不断递归二分求和就可以了,后半部分为幂次式,将在下面第4点讲述计算方法。



(2)若n为偶数,一共有奇数项,则:

1 + p + p^2 + p^3 +...+ p^n

= (1+p^(n/2+1)) + p * (1+p^(n/2+1)) +...+ p^(n/2-1) * (1+p^(n/2+1)) + p^(n/2)

= (1 + p + p^2 +...+ p^(n/2-1)) * (1+p^(n/2+1)) + p^(n/2);
->偶数时以n/2为中点二分 中点n/2需额外加上

上式红色加粗的前半部分恰好就是原式的一半,依然递归求解

知道以上三点就好办了,代码量如开挂般少。。。(注意上long long 乘中会爆

代码如下:

#include <iostream>
#include <cstdio>
#include <cstring>
#define mod 9901
#define ll long long

using namespace std;

ll pow(ll a,ll b)//快速幂
{
    ll ans = 1;
    while(b)
    {
        if(b&1) ans = ans*a%mod;
        a = a*a%mod;
        b >>= 1;
    }
    return ans;
}

ll sum(ll p,ll k)//二分求等比数列前n项和
{
    if(!k) return 1;
    if(k&1) return sum(p,k/2)*(1+pow(p,k/2+1))%mod;
    else return (sum(p,k/2-1)*(1+pow(p,k/2+1))+pow(p,k/2))%mod;
}

ll make(ll a,ll b)//处理a的分解+答案
{
    int i;
    ll cnt,ans = 1;
    for(i = 2; i*i <= a; )//根号法+奇偶法
    {
        cnt = 0;
        while(a%i == 0)
        {
            a /= i;
            cnt++;
        }
        if(cnt) ans = ans*sum(i,b*cnt)%mod;
        if(i == 2) i++;//简单的奇偶优化
        else i += 2;
    }

    if(a != 1) ans = ans*sum(a,b)%mod;
    return ans;
}

int main()
{
    ll a,b;
    scanf("%lld %lld",&a,&b);
    printf("%lld\n",make(a,b));
    return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: