题目描述:
题目分析:
个人思路竞赛时思路:
首先做的时候只是注意到了f(n,x) = n -> x 是质数,想以此为突破点,对于每一个数 num 找到 它的因子 y 来递归求解
但是发现这样方法数求解方法数不大好判断所以,还是没有能解决问题
学习 acwing_yxc 的解题思路 :(所需要的算法中的知识点都在最底下进行描述)
对于 长度 n 数 x:
1. 找到 x 的全部质因数的个数 : 即 x = p1^a1 * p2^a2 * ... * pn^an (pi为质因子,ai为其对应的个数) ,然后相当于在长度 n 的甲板上面放置 a1+...+an = S 个质因子,求解放置的方案数
(直接找到全部的质因子,而不是单纯的找到一个然后递归)
找质因子 :
// 对 n 求解质因数
vector<vector<int>> prime_factor;
// prime_factor[i] = {xi,ni} 质因数 xi 的个数是 ni 个
for(int i = 2;i < n;i++) {
if(n%i == 0) {
int len = 1;
n /= i;
while(n%i == 0) {
n/=i;
len++;
}
prime_factor.push_back({i,len});
}
}
2. 对于a1 个 p1 ,使用 隔板法来计算放置方法个数:
对于n个位置上进行放置 a1 个 p1 , 每个位置上有 xi 个p1 得:x1+x2+... +xn = a1
则:(x1 + 1) + (x2 + 1) + ... + (xn + 1) = a1 + n 这两个方程是等价的
那么就是如下图隔板法所示:(a1 + n)可以有(a1+n-1)个空隙,求这些空隙中插入 n - 1 个隔板的插入方案数即可
即:C(n-1,n+a1-1) 求组合数的方法在最底下
然后对于其他的 pi 一样求出方案数,最后全部累乘起来即可
求组合数C(a,b)
首先通过公式可以得到C(a,b) = a! / (b! * (a-b)!)
所以我们可以预处理将数据范围内的f(n) = n! % MOD 和 g(n) = (n!)-1 % MOD 提前求出来,然后直接计算C(a,b) = f(a)*g(b)*g(a-b) % MOD 即可
求逆元的操作
参考博客:
当且仅当 gcd(a,p)=1 时,a 在模 p 情况下有逆元。
方法一:快速幂(qmi)费马小定理求解 O(logN)
const int MOD = 9e+7;
typedef long long ll;
// 或者自己定义一个素数MOD
int exe_mi(int a,int b) {
// return a^b;
int res = 1;
while(b!=0) {
if(b&1) {
res = (res*a)%MOD;
}
a = (ll(a*a))%MOD;
b >>= 1;
}
return res;
}
int qmi(int a) {
return exe_mi(a,MOD - 2);
}
补充:则对于一个阶乘来求解逆元,n! 得逆元就是 (n!)^(p-2) 然后利用快速幂即可,注意预先处理 n!
for(int i = 2;i < 10005;i++) {
f[i] = (f[i-1]*i) %MOD;
g[i] = exe_mi(f[i],MOD-2);
}
方法二 :扩展欧几里得算法算法求逆元
看上面博客即可
方法三:线性求1~p-1的逆元
按照上面的方法,如果我们要求 1 到 p−1 关于 p 的逆元,而 p 较大时,时间复杂度有点吃不消。
而我们有一种更强的做法,可以在 O(p) 的时间内解决。对于当前的 i ,我们设 p=k×i+r 。于是:
而 r 一定是小于 i 的,所以求解上来时,r 是之前已经求得的数,可以直接利用
而初始化是 : f[1] = 1 -> 1 的逆元是 1,然后遍历求解即可:
比如对于
p = 17,则 f[2] = -((17/2) * (17%2)^-1) MOD p = 9
p = 17,则f[3] = -((17/3) * (17%3)^-1) MOD p = (-5*9) % p = 6
经过验证正确
// 利用公式法求解逆元
int f[p];
const int p = 17;
int main() {
f[1] = 0;
for(int i = 2;i < p;i++) {
f[i] = (p + (-1*(p/i))*f[p%i])%p;
}
}