先来了解几个概念:排列数,组合数。

一、定义及有用的性质

排列数:从n个不同元素中依次取出m个元素排成一列的方案数。P(n,m)=n!/(n-m)!

组合数:从n个不同元素中依次取出m个元素形成一个集合的方案数。(注意,集合满足无序性,这是和排列数的区别)。C(n,m)=n!/m!(n-m)!

组合数性质

  性质1 C(n,m)= C(n,n-m)

  性质2 C(n,m)=C(n-1,m-1)+C(n-1,m)

  性质3 C(n,0)+C(n,1)+C(n,2)+...+C(n,n)=2^n(道出组合数与杨辉三角间的联、系)

二、组合数球阀

① 递推 复杂度为n²

  c[i][j]=c[i-1][j]+c[i-1][j-1]

但是要注意要命的初始化--

丢一段代码跑。

② 预处理阶乘+逆元 复杂度为nlogn

首先我们应该知道,除以一个数等于乘上这个数的逆元,那么我们就可以直接(生猛 地利用原始带有阶乘的公式,分母我们处理逆元。这里用到的是最简单的费马小定理方法,一个数x在膜p意义下的逆元等于x^(p-2)

丢一段代码跑。这个方法的使用条件是p为素数

 #include<cstdio>
#include<algorithm> using namespace std;
typedef long long ll;
const int p=1e9+; ll n,k,x;
ll ans=; ll ksm(ll a,ll b)
{
ll tmp=;
while(b)
{
if(b&) tmp=tmp*a%p;
b>>=;
a=a*a%p;
}
return tmp;
} int main()
{
//求C(n,k)
scanf("%d%d",&n,&k);
for(int i=;i<=n;i++) ans=ans*i%p;
for(int i=;i<=k;i++) ans=ans*ksm(i,p-)%p;
for(int i=;i<=n-k;i++) ans=ans*ksm(i,p-)%p;
printf("%lld",ans);
return ;
}

*Update 费马小定理有的时候可能会复杂度爆炸 这里介绍一种exgcd求逆元的方法

 ll exgcd(ll a,ll b,ll &x,ll &y)
{
if(b==)
{
x=;
y=;
return a;
}
int gu=exgcd(b,a%b,x,y);
int t=x;
x=y;
y=t-a/b*y;
return gu; } ll niyuan(ll hu)
{
x=,y=;
ll tmp=exgcd(hu,p,x,y);
return (x+p)%p;
} ll C(ll k,ll m)
{
ll up=fac[k]%p;
ll down=fac[m]%p*fac[k-m]%p;
ll ans=up*niyuan(down)%p;
return ans;
}

③ 分解质因数 复杂度为nlogn

前导芝士:算术分解定理。

我们把分子分母都进行分解质因数,把整个分子分母对应的质因数的指数相减(消去)

丢一段代码跑。

 #include<cstdio>
#include<algorithm>
using namespace std;
typedef long long LL;
int n,m;
int p;
int i,j;
int tot;
int prim[],cnt[];
bool flag[]; void fj(int a,int k)//分解质因数 k=1/-1
{//k==1时为分子操作 质因子数++
//k==-1时为分母操作 质因子数--
int x=a;
for (int i=;i<=tot;i++)
{
if (x%prim[i]==)
{
while (x%prim[i]==) x/=prim[i],cnt[prim[i]]+=k;
}
if (x==) break;
if (prim[i]*prim[i]>n) break;
}
if (x>) cnt[x]+=k;
} int Pow(int x,int a)
{
int ans=; int j=;
while (j<=a)
{
if (j&a) ans=((LL)ans*(LL)x)%p;
j<<=;
x=((LL)x*(LL)x)%p;
}
return ans;
} int main()
{
scanf("%d%d%d",&n,&m,&p);
//筛素数
for (i=; i<=n; i++)
{
if (!flag[i]) prim[++tot]=i;
for (j=; j<=tot; j++)
{
if (prim[j]*i>n) break;
flag[prim[j]*i]=;
if (i%prim[j]==) break;
}
}
//printf("/////");
int a=m; int b=(n-m);
int c=max(a,b);
//一定有一部分会自己消去(上下完全相同)
for (i=c+; i<=n; i++)
{//分子操作
fj(i,);
}
//分母操作
for (i=;i<=min(a,b);i++) fj(i,-);
int ans=;
for (i=; i<=n; i++)
{
if (cnt[i]>) ans=((LL)ans*Pow(i,cnt[i]))%p;
}
printf("%d\n",ans);
return ;
}

组合数的几种球阀 By cellur925的更多相关文章

  1. 浅谈Floyd的三种用法 By cellur925

    Floyd大家可能第一时间想到的是他求多源最短路的n³算法.其实它还有另外两种算法的嘛qwq.写一发总结好了qwq. 一.多源最短路 放段代码跑,注意枚举顺序,用邻接矩阵存图.本质是一种动规. 复杂度 ...

  2. Adjacent Bit Counts(01组合数)

    Adjacent Bit Counts 4557 Adjacent Bit CountsFor a string of n bits x 1 , x 2 , x 3 ,..., x n , the a ...

  3. [数]数学系列预习->补水题ver.

    ---恢复内容开始--- 话说要学反演了,contest一题都搞不定,整理题目暂且搁置,数学笨蛋来学一下数学_(:з」∠)_ ---恢复内容结束--- 是的,预习看了半天教学,没有整理,做题又都不会, ...

  4. java小程序(课堂作业02)

    1,三种方法计算组合数 ①设计思路:第一种方法就是通过阶乘公式然后运用公式计算出组合数,第二种通过公式推导出cnk=n/(n-k)cnk-1,然后然后从ckk 开始运算到cnk,第三种方法就是通过递归 ...

  5. 一种递推组合数前缀和的Trick

    记录一下一种推组合数前缀和的方法 Trick 设\(\sum_{i = 0}^m C_n^i = S(n, m)\) \(S\)是可以递推的 \(S(n, m + 1) = S(n, m) + C_{ ...

  6. 1-求组合数(c(n, m))的几种方法

    1.求C(n, m) 动态规划(递归+记忆数组) 递推关系为:C(n, m) = C(n-1, m) + C(n - 1, m - 1),C(n, m)表示为从n个数中选出m个出来,可以基于最后一个元 ...

  7. 组合数C(n,m)的四种求解方法

    转自:文章 1.暴力求解 C(n,m)=n*(n-1)*...*(n-m+1)/m!,(n<=15): int CF(int n,int m) { ,i,j; ;i--) ans*=i; ;i- ...

  8. LCM性质 + 组合数 - HDU 5407 CRB and Candies

    CRB and Candies Problem's Link Mean: 给定一个数n,求LCM(C(n,0),C(n,1),C(n,2)...C(n,n))的值,(n<=1e6). analy ...

  9. 计算一维组合数的java实现

    背景很简单,就是从给定的m个不同的元素中选出n个,输出所有的组合情况! 例如:从1到m的自然数中,选择n(n<=m)个数,有多少种选择的组合,将其输出! 本方案的代码实现逻辑是比较成熟的方案: ...

随机推荐

  1. codechef Taxi Driver

    题意: 给N个点求任意两个点的“距离”总和: A,B的“距离”定义为:min(|ax-bx|,|ay-by|) (n<200000) 好题! 解析: 看着没思路 先是公式化简:让 ax=sx+s ...

  2. 关于用String Calender类 计算闰年的Demo

    package cn.zmh.zuoye; import java.util.Calendar; public class StringRun { public static void main(St ...

  3. 【.Net 学习系列】-- EF Core实践(Code First)

    一.开发环境: vs2015, .Net Framework 4.6.1 二.解决方案: 新建一个控制台应用程序 添加引用:Microsoft.EntityFrameworkCore.SqlServe ...

  4. eclipse环境下无法创建android virtual Devices(AVD)问题解决的方法汇总

    首先,要在eclipse环境下成功的创建一个安卓虚拟机,须要有三项东西,第一就是eclipse,第二就是android SDK Manager,第三就是ADT,也就是eclipse环境下的一个安卓虚拟 ...

  5. Null value was assigned to a property of primitive type setter of原因及解决

    出现Null value was assigned to a property of primitive type setter of错误是由于类型不匹配,将字段的属性由hibernate的int类型 ...

  6. Android时时监測手机的旋转角度 依据旋转角度确定在什么角度载入竖屏布局 在什么时候载入横屏布局

    一.场景描写叙述: 最近开发中遇到个问题,就是我们在做横竖屏切换的功能时.横竖屏布局是操作系统去感知的,作为开发员没法确定Activity在什么时候载入横屏布局,在什么时候载入竖屏布局.因此为了找到载 ...

  7. (void __user *)arg 中__user的作用

    __user宏简单告诉编译器(通过 noderef)不应该解除这个指针的引用(因为在当前地址空间中它是没有意义的). (void __user *)arg 指的是arg值是一个用户空间的地址,不能直接 ...

  8. MySQL基础笔记(二) 完整性约束

    我们知道,一种数据模型必须包含三个基本的部分: 构造机制(数据结构):主要描述数据的类型.内容.性质以及数据间的联系等. 运算机制(数据操作):主要描述在相应的数据结构上的操作类型和操作方式. 约束机 ...

  9. PHPCMS中GET标签概述、 get 标签语法、get 标签创建工具、get 调用本系统演示样例、get 调用其它系统演示样例

    一.get 标签概述 通俗来讲,get 标签是Phpcms定义的能直接调用数据库里面内容的简单化.友好化代码,她可调用本系统和外部数据,仅仅有你对SQL有一定的了解,她就是你的绝世好剑!也就是适合熟悉 ...

  10. 工作总结 使用html模板发邮件 前面空一大块

    HTML邮件的本质其实是发送了一个html页面.邮件的空白必然是页面的空白,所以你要找到你发送邮件的html模板所在,然后去掉空白即可,如果这是一个公共文件,需要注意你往往用的只是你的部分,很大程度还 ...