简单组合计数
简单组合计数
组合计数基础
几个原理:
1.加法原理:若完成一件事有\(n\)类不同的方法,第\(i\)类方法有\(a_i\)种方法,且这些方法互不重合则完成这件事共有\(\sum_{i=1}^na_i\)种方法
2.乘法原理:若完成一件事有\(n\)个不同的步骤,每个步骤有\(a_i\)种完成方法,且互不干扰,则完成该件事共有\(\prod_{i=1}^na_i\)种方法
几个定义:
1.排列数:从\(n\)个不同的数里依次 取出\(m\)个,产生的不同排列数量为:
注意是\(m\)在上
2.组合数:从\(n\)个不同的数里取出\(m\)个组成一个集合(不考虑顺序,只要元素到位),产生的不同集合数量为:
性质:
1.\(C_n^m=C_n^{n-m}\)
2.\(C_n^m=C_{n-1}^m+C_{n-1}^{m-1}\) 重要
3.\(\sum_{i=0}^nC_n^i=2^n\)
4.\(\sum_{k=0}^n(-1)^kC_n^k=0\)
5.\(C_{m+n}^k=\sum_{j=0}^k(C_m^{k-j}\times C_n^j)\)
6.\(C^{k+1}_ {n+1} =\sum_{i=k}^nC_i^k\)
7.Lucas定理:若\(p\)是质数,则对于任意整数\(1\le m\le n\)有
这个注意代码实现的时候得递归实现
二项式定理
多项式定理
设\(n\)是正整数,则对于所有的\(x_i\) \((i\in [1,t])\)有
这里把\(n_i\)的所有组合枚举出来即可
多重集的排列数
多重集是允许包含重复元素的集合,设集合\(\mathbb{S}={\lbrace n_1·a_1,n_2·a_2,n_3·a_3…n_k·a_k\rbrace}\)是由\(n_1\)个\(a_1\)……组成的多重集,\(n=\sum_{i=1}^kn_i\)则它的全排列个数为:
多重集的组合数
设集合\(\mathbb{S}={\lbrace n_1·a_1,n_2·a_2,n_3·a_3…n_k·a_k\rbrace}\)是由\(n_1\)个\(a_1\)……组成的多重集,\(n=\sum_{i=1}^kn_i\)
则从该集合中选出\(r(r\le n)\)个数组成的不同的多重集数量为:
Catlan数
定义
与其有关的问题:
1.给定n个左括号和n个右括号组成合法的括号序列的个数为\(Cat_n\)
2.\(1\sim n\)依次进栈,合法的出栈序列个数为\(Cat_n\)
3.n个节点组成的不同二叉树的数量是\(Cat_n\)
4.在平面直角坐标系中每一步只能向上或者向右走,从点\((0,0)\)到点\((n,n)\)并且除了两个端点之外路线上没有一个点经过直线y=x,这样的路径条数一共有\(2Cat_{n-1}\)
几个结论:
1.将一个长度为n的环变作n个自环至少要将节点交换n-1次
2.设\(F_n\)表示用最少步骤将一个长度为n的环变作n个自环的操作方案数,有:\(F_n=n^{n-2}\)
3.遇到序列上的错位,需要交换位置还原的问题可以往图论方向想
exlucas
用途:快速求出以下式子的值(p不一定是质数)
遇到这种情况,我们可以将p进行算术基本定理的质因数分解。设\(p=\prod_{i=1}^kp_i^{c_i}\),我们设\(a_i=C_n^m \bmod p_i^{c_i}\)
这样我们就把问题简化成了:
这个一次同余式组直接用中国剩余定理\(CRT\)求即可
问题在于如何求出\(C_n^m \bmod p_i^{c_i}\)
先来看简化版扩展卢卡斯:
题意:给定整数\(n,q(n,q\le 10^9)\)计算
因为999911659是一个质数,由扩欧得:
原式等价于
所以本题关键在于计算\(\sum_{d|n}C_n^d \bmod 999911658\)
按照扩展卢卡斯的思路,我们将999911658分解质因数发现:\(999911658=2\times 3\times 4679 \times 35617\)
我们枚举n的约数d,分成四个,运用Lucas定理分别计算组合数\(C_n^d\)对这四个质因数的模,求出\(\sum_{d|n}C_n^d\)模这四个质数的值,记作\(a_1,a_2,a_3,a_4\)
当然对于质数的逆元啥的自己预处理一下就可以
现在我们的问题就是求解同余方程组
然后用快速幂进行计算即可
#define int long long
int mod=999911659,p[4]={2,3,4679,35617},n,m,cnt,ans,q;
int ys[40000],jc[40000],a[4],x,y,t;
int power(int a,int b,int p){
int ans=1;
while(b){
if(b&1)ans=ans*a%p;
a=a*a%p;
b>>=1;
}
return ans;
}
int C(int n,int m,int p){
return n<m?0:jc[n]*power(jc[m]*jc[n-m],p-2,p)%p;
}
int lucas(int n,int m,int p){
return m?C(n%p,m%p,p)*lucas(n/p,m/p,p)%p:1;
}
int exgcd(int a,int b,int &x,int &y){
if(!b){
x=1,y=0;
return a;
}
int d=exgcd(b,a%b,x,y);
int t=x;
x=y,y=t-(a/b)*y;
return d;
}
signed main(){
scanf("%lld%lld",&n,&q);
if(q%mod==0){
puts("0");
return 0;
}
jc[0]=1;
m=sqrt(n);
for(int i=1;i<=m;i++){
if(n%i==0){
ys[++cnt]=i,ys[++cnt]=n/i;
}
}
cnt-=ys[cnt-1]==ys[cnt];
for(int i=0;i<4;i++){
for(int j=1;j<p[i];j++){
jc[j]=jc[j-1]*j%p[i];
}
for(int j=1;j<=cnt;j++){
a[i]=(a[i]+lucas(n,ys[j],p[i]))%p[i];
}
exgcd((mod-1)/p[i],p[i],x,y);
ans=(ans+a[i]*(x%p[i]+p[i])*(mod-1)/p[i])%(mod-1);
}
printf("%lld\n",power(q,ans,mod));
return 0;
}
这也就是所有的\(c_i=1\)时的简化情况