大数阶乘 快速阶乘

大数模拟阶乘

一位一位的乘、进位。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e4 + 5;
int a[maxn];
int main()
{
    int n;
    while(cin >> n)
    {
        memset(a, 0, sizeof(a));
        a[1] = 1;
        int i, j, len = 1, rest;
        for(i = 2; i <= n; ++i)
        {
            rest = 0;
            for(j = 1; j <= len; ++j)//模拟一位一位乘
            {
                a[j] = a[j] * i + rest;
                rest = a[j] / 10;
                a[j] = a[j] % 10;
            }
            while(rest)//后面如果有进
            {
                a[j++] = rest % 10;
                rest /= 10;
            }
            len = j - 1;
        }
        for(i = len; i >= 1; --i)
            cout << a[i];
        cout << endl;
    }
    return 0;
}

阶乘取模

很简单的思想,直接举例

题目:求 (1e12 + 10) ! % mod , mod = 1e6.

思路:直接模拟会超时

首先,求 1 ! ~ 1e6! % mod , 存放在数组pre_mul[ ]中,这个只要花费线性的时间O(mod)。
易知
( 1 ∗ 2 ∗ 3 ∗ … … ∗ 1 e 6 ) % m o d = ( 1 e 6 + 1 ) ∗ ( 1 e 6 + 2 ) ∗ ( 1 e 6 + 3 ) ∗ … … ∗ ( 1 e 6 + 1 e 6 ) ) % m o d (1 * 2 * 3 * …… * 1e6 ) \% mod = (1e6 + 1) * (1e6 + 2) * (1e6 + 3) * …… * (1e6 + 1e6) ) \% mod (1231e6)%mod=(1e6+1)(1e6+2)(1e6+3)(1e6+1e6))%mod
所以最终答案为

(((1e12 + 10) / 1e6  * pre_mul[1e6]) % mod * pre_mul[(1e12 + 10) % 1e6] )% mod

这种做法适用于mod比较小、在1e8范围里面的情况。


阶乘取大模

如果mod为1e9、1e10的数量级,上面的方法就不适用了,要另寻途径。

对于某个阶乘,我们将它拆分成质因数幂次方的乘积,例如 :
6!
=1 * 2 * 3 * 4 * 5 * 6
=1 * 24 * 32 * 5

容易想到,若把每个数的质因数都分解出来,并且统计每种质因子有多少个,我们就可以多次使用二分求幂,再把它们的结果乘起来。
注意这里并不是真的要老老实实地去分解每个数的质因子。对于每个质数x,我们可以很快算出前n个正整数一共包含有多少个质因子x

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
const ll maxn = 1e4 + 5;
int prime[2000];
bool not_prime[maxn];

int cnt = 0;
void init()
{
    not_prime[0] = not_prime[1] = 1;
    for(ll i = 2; i < maxn; i++)
    {
        if(!not_prime[i])
            prime[cnt++] = i;
        //关键处1
        for(ll j = 0; j < cnt && i * prime[j] < maxn; j++)
        {
            not_prime[i * prime[j]] = 1;
            if(!(i % prime[j]))  //关键处2
                break;
        }
	}
}
ll quick_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;
}
int main()
{
    init();
    int n;
    while(cin >> n)
    {
        ll ans = 1;
        for(int i = 0; i < cnt && prime[i] <= n; ++i)
        {
            ll k = 0, t = n;
            while(t)
            {
                k += t / prime[i];
                t /= prime[i];
            }
            ans = ans * quick_pow(prime[i], k) % mod;
        }
        cout << ans << endl;
    }
    return 0;
}

参考来源

博客
https://blog.csdn.net/deaidai/article/details/79253753

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值