POJ 1845 Sumdiv(求阶乘的因子和)

题目链接:http://poj.org/problem?id=1845

一道不错的数论题,用到的知识比较多,表示也是看了题解之后才做出了这道题。之前对因子和还只是有那么一丁点概念,现在概念好像明朗了许多。

题意简单明了,求A^B的因子和,然后对9901取模。

先解释下因子和的概念,对于一个数N:
N的因子和为(1+p1+p1^2+…+p1^c1)*(1+p2+p2^2+…+p2^c2)*… *(1+pk+pk^2+…+pk^ck)

Pi表示N的第i种素因子,Ci表示N的第i种素因子的个数。

那么按照解题思路来说,我们要先将A分解素因子,进而将A分解成p1^c1+p2^c2+...+pk^ck的唯一分解方式,然后求每个素因子pi从0次幂到ci次幂的和,最终将这些和相乘得到结果。

那么,让我们由果溯因的推出顺序:

1.要想将所有幂次和相乘,我们只需一个for循环即可。

2.要想求某一个素因子pi,0到ci次幂的和,我们最容易想到的是等比求和公式,但是根据模运算的运算规则:

(a + b) % p = (a % p + b % p) % p (1)(a - b) % p = (a % p - b % p) % p (2)

(a * b) % p = (a % p * b % p) % p (3)a ^ b % p = ((a % p)^b) % p (4)

它与基本四则运算有些相似,但是除法例外。

所以等比求和公式因为包含除法而无法做到边算边取模,于是,我们应当采取一种分治的策略,来节省时间复杂度,并且保证该算法中不含有除法,即二分递归求解等比数列的和。

3.为了找到所有的素数p,我们需要对A进行分解素因子,时间复杂度是O(sqrt(n))。

4.在递归求和的过程中,必然要算到pi的某次幂,当这个幂次比较大或者求出幂的结果的时候,就需要用到快速幂取模了。

至此,此题已可解。

代码如下:

#include<cstdio>
#include<iostream>
using namespace std;
const __int64 mod=9901;
int p[10010],c[10010];
__int64 pow(__int64 a,__int64 b){//快速幂取模
    __int64 r=1,base=a;
    while(b!=0){
        if(b&1) r=((r%mod)*(base%mod))%mod;
        base=((base%mod)*(base%mod))%mod;
        b>>=1;
    }
    return r;
}
__int64 sum(__int64 a,__int64 b){//递归求等比和
    if(b==0)
        return 1;
    if(b&1)
        return ((1+pow(a,b/2+1))%mod*sum(a,b/2)%mod)%mod;
    else
        return ((1+pow(a,b/2+1))%mod*sum(a,(b-1)/2)%mod+pow(a,b/2)%mod)%mod;
}
int main(){
    __int64 A,B,ans;
    int count;
    while(~scanf("%I64d%I64d",&A,&B)){
        count=1,ans=1;
        for(int i=2;i*i<=A;i++){//分解素因子
            if(A%i==0){
                p[count]=i;
                while(A%i==0){
                    A/=i;
                    c[count]++;
                }
                count++;
            }
        }
        if(A!=1){
            count++;
            p[count]=A;
            c[count]++;
        }
        for(int i=1;i<=count;i++)//将所有幂次和相乘
            ans=(ans%mod*sum(p[i],B*c[i])%mod);
        printf("%d\n",ans);
    }
    return 0;
}

表示最近对数论感了点兴趣,嗯,还真是东一榔头,西一扫帚...




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值