ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

求组合数I II III IV

2022-09-13 11:34:30  阅读:203  来源: 互联网

标签:infact int res LL IV II 逆元 III mod


求组合数

求组合数1 递推 $ O(n^2) $

原题链接:https://www.acwing.com/problem/content/887/

思路

数据范围为2000,可以在\(n^2\)以内解决问题,就直接使用下面的递推即可

已知公式

\[C_{a}^{b} = C_{a-1}^{b} + C_{a-1}^{b-1} \]

就用此公式递推求即可

for(int i = 0; i < N; i ++)
{
	for(int j = 0; j <= i; j ++)
	{
		if(j == 0) c[i][j] = 1;
		else c[i][j] = (c[i-1][j] + c[i-1][j-1]) % mod;
	}
}
代码:
#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;

const int N = 2010;
const int mod = 1e9+7;

int c[N][N];
int n;

void init()
{
    for(int i = 0; i < N; i ++)
    {
        for(int j = 0; j <= i; j ++)
        {
            if(j == 0) c[i][j] = 1;
            else c[i][j] = (c[i-1][j] + c[i-1][j-1]) % mod ;
        }
    }
}

int main()
{
    cin >> n;
    
    init();
    
    while(n --)
    {
        int a,b;
        cin >> a >> b;
        cout << c[a][b] << endl;
    }
    
    return 0;
}

求组合数2 \(O(nlog(n))\)

原题链接:https://www.acwing.com/problem/content/888/

逆元

(快速幂与快速幂求逆元)

如果 \(b*x \equiv 1 \: mod\: N\) 或者\(\frac{1}{b} \equiv x\: mod\: N\)
则称 x 为b模N的乘法逆元

思路

数据范围是100000,\(n^2\)时间肯定不行

可以根据以下公式预处理出来阶乘(分母上的阶乘使用快速幂求逆元),时间复杂度为\(nlog(n)\)

已知有公式

\[C_{a}^{b} = \frac{a!}{(a-b)!b!} \]

可以预处理出来阶乘,但是最后计算的时候不能直接除以相应的阶乘
因为:(a / b) % p 不等于(a % p) / (b % p),程序中算出来的每一个 i 的阶乘都是模了 mod 的,不能相除,只能转化为逆元求解

所以计算(a-b)!模N的乘法逆元和b!模N的乘法逆元,因为1e9+7是一个质数,比一个质数小的数都与这个数互质,所以直接用快速幂求逆元

阶乘的逆元的计算:(阶乘的逆元可以用前面的逆元来计算)
如果x 是 a 的逆元,y 是 b 的逆元,那么 ax≡1,by≡1ax≡1,by≡1,所以 abxy≡1,所以 xy 是 ab 的逆元。–yxc

思路实现

预处理阶乘

const int mod = 1e9+7; // 是一个质数

int fact[N],infact[N];
fact[0] = infact[0] = 1;
for(int i = 1; i < N; i ++)
{
	fact[i] = (LL)fact[i-1] * i % mod;
	infact[i] = (LL)infact[i-1] * qmi(i,mod - 2,mod) % mod;
}

快速幂公式

int qmi(int a,int k,int p)
{
	int res = 1;
	while(k)
	{
		if(k & 1) res = (LL)res * a % p;
		a = (LL) a * a % p;
		k >>= 1;
	}
	return res;
}
代码
#include<iostream>
#include<algorithm>

using namespace std;

typedef long long LL;

const int N = 100010;
const int mod = 1e9 + 7;

int fact[N],infact[N];

int qmi(int a,int k,int p)
{
    int res = 1;
    while(k)
    {
        if(k & 1) res = (LL) res * a % p;
        a = (LL) a * a % p;
        k >>= 1;
    }
    return res;
}

int main()
{
    fact[0] = infact[0] = 1;
    for(int i = 1; i < N; i ++)
    {
        fact[i] = (LL)fact[i-1] * i % mod;
        infact[i] = (LL)infact[i-1] * qmi(i,mod - 2,mod) % mod;
    }
    
    int n;
    scanf("%d",&n);
    
    while(n --)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        printf("%d\n",(LL)fact[a] * infact[a-b] % mod * infact[b] % mod);
    }
    
    return 0;
}

求组合数3 \(O(p∗logn∗logp)\)

原题链接:https://www.acwing.com/problem/content/889/

卢卡斯定理lucas

\[C_{a}^{b} \equiv C_{a\;mod\;p}^{b\;mod\;p} * C_{a/p}^{b/p} \]

使用卢卡斯定理,可以将时间复杂度变成\(p*logn*logp\)

当b < p && a < p的时候直接计算这个组合数即可
否则计算 \(C_{a\;mod\;p}^{b\;mod\;p} * lucas(C_{a/p}^{b/p})\)

代码
#include<iostream>

using namespace std;

typedef long long LL;

int qmi(int a,int k,int p)
{
    int res = 1;
    while(k)
    {
        if(k & 1) res = (LL) res * a % p;
        a = (LL) a * a % p;
        k >>= 1;
    }
    return res;
}


int C(int a,int b,int p)
{
    if(b > a) return 0;
    
    int res = 1;
    for(int i = 1, j = a; i <= b; i ++,j --)
    {
        res = (LL)res * j % p;
        res = (LL)res * qmi(i,p-2,p) % p;
    }
    return res;
}

int lucas(LL a, LL b, int p)
{
    if(a < p && b < p) return C(a,b,p);
    else return (LL)C(a % p,b % p,p) * lucas(a/p,b/p,p) % p;
}

int main()
{
    int n;
    cin >> n;
    for(int i = 0; i < n; i ++)
    {
        LL a,b;
        int p;
        cin >> a >> b >> p;
        cout << lucas(a,b,p) << endl;
    }
    
    return 0;
}

求组合数4 分解质因数+高精

原题链接:https://www.acwing.com/problem/content/890/

思路

这个题没有要求mod一个数

a和b可以取到5000,结果会很大。

可以将公式\(C_{a}^{b} = \frac{a!}{(a-b)! * b!}\) 的分子分母分解质因数 , 最后让质因子们相乘就可以得到答案.

质因子相乘需要使用高精度乘法

如何求得\(a!\)的质因子们的个数

比如质因子p让 a 分别除以 p^1、p^2、p^3...p^n(直到p^n大于a)
就可以得到a!里面有几个p

例如 12! =  12*11*10*9*8*7*6*5*4*3*2*1
除以 2^1 2^2 2^3就可以得到12阶乘里面有几个2
代码
#include<iostream>
#include<vector>
#include<algorithm>

using namespace std;

const int N = 5010;

int primes[N],cnt;
bool st[N];

int sum[N];

void get_primes(int n)
{
    for(int i = 2; i <= n; i ++)
    {
        if(!st[i]) primes[cnt ++] = i;
        for(int j = 0; i * primes[j] <= n ; j ++)
        {
            st[primes[j] * i] = true;
            if(i % primes[j] == 0) break;
        }
    }
}


// 求x!中有多少个p
int get(int x, int p) 
{
    int res = 0;
    while(x)
    {
        res += x / p;
        x /= p;
    }
    return res;
}

// 高精度乘法
vector<int> mul(vector<int> a,int b)
{
    vector<int> c;
    int t = 0;
    for(int i = 0; i < a.size(); i ++)
    {
        t += a[i] * b;
        c.push_back(t % 10);
        t /= 10;
    }
    
    while(t)
    {
        c.push_back(t % 10);
        t /= 10;
    }
    return c;
}

int main()
{
    int a,b;
    cin >> a >> b;
    
    // 素数筛
    get_primes(a);
    
    // 求a!、(a-b)!、b!里有多少个素数质因子=>求Cab中有多少个p
    for(int i = 0; i < cnt; i ++)
    {
        int p = primes[i];
        sum[i] = get(a,p) - get(a-b,p) - get(b,p);
    }
    
    
    vector<int> res;
    res.push_back(1);
    // 高精度乘法求结果
    for(int i = 0; i < cnt; i ++)
    {
        for(int j = 0; j < sum[i]; j ++)
        {
            res = mul(res,primes[i]);
        }
    }
    
    for(int i = res.size() - 1; i >= 0; i --) cout << res[i];
    
    return 0;
}

标签:infact,int,res,LL,IV,II,逆元,III,mod
来源: https://www.cnblogs.com/rdisheng/p/16684068.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有