大数模拟阶乘
一位一位的乘、进位。
#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
(1∗2∗3∗……∗1e6)%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;
}