模板:组合数学

组合数学

组合数取模

为方便, C n m 记为 C ( n , m )

struct Combination//底数为0~n都求出来,对p取模
{
    vector<vector<ll> > c;
    Combination(int n,ll p):c(n+1,vector<ll>(1,1))//答案在c[n][m]中,时间复杂度O(N*N)
    {
        for(int i=1; i<=n; c[i++].push_back(1))
            for(int j=1; j<i; ++j)
                c[i].push_back((c[i-1][j-1]+c[i-1][j])%p);
    }
};
struct CombinatorialNumber//节省空间,只保存以n为底对p取模的结果,时间复杂度O(NlogN),其中log来自于乘法逆元
{
    vector<ll> c;
    CombinatorialNumber(int n,ll p):c(n+1,1)
    {
        for(int i=1; i<=n; ++i)
            c[i]=c[i-1]*(n-i+1)%p*inv(i,p)%p;
    }
};
ll lucas(ll n,ll m,ll p)//卢卡斯定理求C(n,m)%p,时间复杂度O(min(n,p))
{
    if(!m)return 1;
    if(n<m||n%p<m%p)return 0;
    if(n>=p||m>=p)return lucas(n/p,m/p,p)*lucas(n%p,m%p,p)%p;
    ll a=1,b=1;//此时有0<m<n<p,可考虑优化用上面的结果来求
    for(m=min(m,n-m); m;)//暴力求
        a=a*n--%p,b=b*m--%p;
    return a*inv(b,p)%p;
}

Stirling数

第一类斯特林数

第一类斯特林数 S ( p , k ) 的一个的组合学解释是:将p个物体排成k个非空循环排列的方法数。
递推公式: S ( p , k ) = ( p 1 ) S ( p 1 , k ) + S ( p 1 , k 1 ) , 1 k p 1 ; S ( p , 0 ) = 0 , p 1 ; S ( p , p ) = 1 , p 0

第二类斯特林数

第二类斯特林数 S ( p , k ) 的一个的组合学解释是:将p个物体划分成k个非空不可辨别的(可以理解为盒子没有编号)集合的方法数。
递推公式: S ( p , k ) = k S ( p 1 , k ) + S ( p 1 , k 1 ) , 1 k p 1 ; S ( p , 0 ) = 0 , p 1 ; S ( p , p ) = 1 , p 0
卷积形式: S ( n , m ) = 1 m ! k = 0 m ( 1 ) k C ( m , k ) ( m k ) n = k = 0 m ( 1 ) k k ! ( m k ) n ( m k ) !
同时有转化: x k = i = 1 k i ! C ( x , i ) S ( k , i )

斯特林近似公式

n ! 2 π n ( n e ) n

小球入盒问题

k 个球 m个盒子 空盒子 方案数
各不相同 各不相同 允许 m k
各不相同 各不相同 m ! S t i r l i n g 2 ( k , m )
各不相同 完全相同 允许 i = 1 m S t i r l i n g 2 ( k , i )
各不相同 完全相同 S t i r l i n g 2 ( k , m )
完全相同 各不相同 允许 C ( m + k 1 , k )
完全相同 各不相同 C ( k 1 , m 1 )
完全相同 完全相同 允许 1 ( 1 x ) ( 1 x 2 ) . . . ( 1 x m ) x k
完全相同 完全相同 x m ( 1 x ) ( 1 x 2 ) . . . ( 1 x m ) x k

Catalan数

h 1 = 1 , h n = 4 n 2 n + 1 h n 1 = C ( 2 n , n ) n + 1 = C ( 2 n , n ) C ( 2 n , n 1 )
在一个格点阵列中,从(0,0)点走到(n,m)点且不经过对角线x=y的方法数: C ( n + m 1 , m ) C ( n + m 1 , m 1 ) , x > y ; C ( n + m , m ) C ( n + m , m 1 ) , x y

Bell数

把n个带标号的物品划分为若干不相交集合的方案数称为贝尔数,其递推公式: B n = i = 0 N 1 C n 1 i B i
下为 O ( P 2 log P ) B n 对P取模。

struct Bell {
    static const int P=999999598,N=7284;
    int a[4],f[N],s[2][N],i,j,x;
    Bell() {
        a[0]=2,a[1]=13,a[2]=5281,a[3]=7283;//P的质因数分解
        f[0]=f[1]=s[0][0]=1,s[0][1]=2;
        for(i=2,x=1; i<N; i++,x^=1)
            for(f[i]=s[x][0]=s[x^1][i-1],j=1; j<=i; ++j)
                s[x][j]=(s[x^1][j-1]+s[x][j-1])%P;
    }
    int cal(int x,ll n) {
        int i,j,k,m=0,b[N],c[N],d[70];
        for(i=0; i<=x; i++)b[i]=f[i]%x;
        while(n)d[m++]=n%x,n/=x;
        for(i=1; i<m; i++)for(j=1; j<=d[i]; j++) {
                for(k=0; k<x; k++)c[k]=(b[k]*i+b[k+1])%x;
                c[x]=(c[0]+c[1])%x;
                for(k=0; k<=x; k++)b[k]=c[k];
            }
        return c[d[0]];
    }
    ll ask(ll n) {
        if(n<N)return f[n];
        ll t=0;
        for(int i=0; i<4; ++i)t=(t+(P/a[i])*pow(P/a[i],a[i]-2,a[i])%P*cal(a[i],n)%P)%P;
        return t;
    }
};

等价类容斥

考虑容斥,Bell(p)枚举所有等价情况。对于一种情况,强制了一个等价类里面的数都要相同,其它的可以相同也可以不同。
容斥系数为: ( 1 ) ( p ) ( 1 ) !
贝尔数前若干项的表: 1 , 2 , 5 , 15 , 52 , 203 , 877 , 4140 , 21147 , 115975 , 678570 , 4213597 , 27644437 , 190899322 , 1382958545

扩展Cayley公式

对于n个点,m个连通块的图,假设每个连通块有a[i]个点,那么用s−1条边把它连通的方案数为 n s 2 a [ 1 ] a [ 2 ] . . . a [ m ]

超立方体

n维超立方体有 2 n i C ( n , i ) 个i维元素。

固定 k 个点为根的带标号有根树森林计数

固定k个点作为根的n个点的带标号有根树森林的方案数是 n n k 1 k

猜你喜欢

转载自blog.csdn.net/w_weilan/article/details/80500741