排列组合

Posted shzr

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了排列组合相关的知识,希望对你有一定的参考价值。

排列组合

  摘要中的问题的答案是$int$;

  组合:$C_n^k=frac{n!}{k!(n-k)!}$

  排列:$A_n^k=frac{n!}{k!}$

  求组合数的方法看起来好像没有什么价值,但是事实上还是挺重要的。

  1.暴力求就不用介绍了吧,一般只适用于数据范围非常小的题目;

  2.取模或不取模都可以用的方法:$C_n^m=C_{n-1}^{m-1}+C_{n-1}^m$.这个做法的思路是这样的:考虑新加进来的第$n$个数的影响,如果选它,那么前$n-1$个中只好少选一个,如果不选它,那么前面就得把$m$个选够,其实是一种动态规划.因为加法可以直接取模,所以对于取模的题目也是非常好用的.注意如果某道题要求采用高精度,就要谨慎的考虑要不要用这种方法了,因为加上高精度之后复杂度会变得非常高,空间开销也非常大.

  3.如果要求取模:预处理阶乘和逆元,$O(N)$预处理,$O(1)$出解,效率非常棒.如果$n$的范围比$p$大很多,可以考虑使用$Lucas$定理;

  4.最坑人的一种:不要求取模,数据范围还挺大的,此时最好,也只能用高精度了.用高精度也是讲求技巧的,如果直接套用公式就需要做很多高精度乘除,而高精度除法的计算效率非常低下.但是没有关系,虽然组合数可能很大,但是它进行唯一分解之后一定是可以用普通的数组存下来的,为什么?因为组合数的所有因子都是之前在$n,k$中出现过的,所以不会太大.而且因为组合数最终是一个整数,所以所有分母上出现的因子都可以在因子中找到并提前除去,虽然除因子的复杂度不是很低,但是总比用高精度除快多了.来一份这种做法的板子吧:

  技术分享图片
 1 void add (int x,int v)
 2 {
 3     for (R i=2;i*i<=x;++i)
 4         while(x%i==0) y[i]+=v,x/=i;
 5     if(x!=1) y[x]+=v;
 6 }
 7 
 8 void mul (int x)
 9 {
10     c[0]+=3;
11     for (R i=1;i<=c[0];++i)
12         c[i]*=x;
13     for (R i=1;i<=c[0];++i)
14         c[i+1]+=c[i]/10,c[i]%=10;
15     while(c[ c[0] ]==0&&c[0]) c[0]--;
16 }
17 
18 void print()
19 {
20     for (R i=c[0];i>=1;--i)
21         printf("%d",c[i]);
22 }
23 
24 for (R i=2;i<=g;++i) add(i,1);
25 for (R i=2;i<=k;++i) add(i,-1);
26 for (R i=2;i<=g-k;++i) add(i,-1);
27 
28 c[0]=c[1]=1;
29 for (R i=2;i<=1000;++i)
30     for (R j=1;j<=y[i];++j)
31         mul(i);
32 print();
高精度组合数

 

  至于排列组合的题目就先咕咕咕了.

 

以上是关于排列组合的主要内容,如果未能解决你的问题,请参考以下文章

离散:常用排列组合模型归纳,DFS代码实现

关于各种排列组合java算法实现方法

html 将以编程方式附加外部脚本文件的javascript代码片段,并按顺序排列。用于响应式网站,其中ma

Java 全排列与组合

c# 排列组合代码类

代码题(19)— 组合与排列