LeetCode 双周赛44场 第四题 5648. 生成乘积数组的方案数

题目描述:

5648. 生成乘积数组的方案数

题目分析:

个人思路竞赛时思路:

首先做的时候只是注意到了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 即可

求逆元的操作

参考博客:

(1) https://www.jianshu.com/p/46a48bbe955a

  (2) https://www.cnblogs.com/chy-2003/p/9656801.html

当且仅当 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;
    }
}

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值