[省选联考 2020 A 卷] 组合数问题

一、题目

点此看题

二、解法

trdnr手把手教你推式子,这就是大佬吧,i了i了

先把多项式展开(算系数对应的贡献):
∑ i = 0 n a i ∑ k = 0 n k i × x k ( n k ) \sum_{i=0}^na_i\sum_{k=0}^nk^i\times x^k\binom{n}{k} i=0naik=0nki×xk(kn)这里需要用到第二类斯特林数的知识,先要康康这个。

感觉一个比较套路的东西是用第二类斯特林数展开幂次,由于斯特林数的下标不能过大(这里展开 x k x^k xk的话斯特林数就会含 k k k,而含 i i i的话要好做些,因为我们要求和 m m m有关的算法),我们只能展开 k i k^i ki
∑ i = 0 n a i ∑ k = 0 n x k ( n k ) ∑ j = 0 k { i j } j ! ( k j ) \sum_{i=0}^na_i\sum_{k=0}^nx^k\binom{n}{k}\sum_{j=0}^k\begin{Bmatrix}i\\j\end{Bmatrix}j!\binom{k}{j} i=0naik=0nxk(kn)j=0k{ij}j!(jk)从斯特林数的意义上考虑,枚举上界可以压一压:
∑ i = 0 n a i ∑ k = 0 n x k ( n k ) ∑ j = 0 i { i j } j ! ( k j ) \sum_{i=0}^na_i\sum_{k=0}^nx^k\binom{n}k{}\sum_{j=0}^i\begin{Bmatrix}i\\j\end{Bmatrix}j!\binom{k}{j} i=0naik=0nxk(kn)j=0i{ij}j!(jk)我们的目标是让 k k k少出现,首先你必须要知道这样一个恒等式:
( n m ) ( m k ) = ( n k ) ( n − k m − k ) \binom{n}{m}\binom{m}{k}=\binom{n}{k}\binom{n-k}{m-k} (mn)(km)=(kn)(mknk)对原式的组合数进行操作,然后交换一波枚举顺序(下面枚举的 k k k即是 k − j k-j kj):
∑ i = 0 n a i ∑ j = 0 i { i j } j ! ( n j ) ∑ k = 0 n − j x k + j ( n − j k ) \sum_{i=0}^na_i\sum_{j=0}^{i}\begin{Bmatrix}i\\j\end{Bmatrix}j!\binom{n}{j}\sum_{k=0}^{n-j}x^{k+j}\binom{n-j}{k} i=0naij=0i{ij}j!(jn)k=0njxk+j(knj)然后使用二项式定理:
∑ i = 0 n a i ∑ j = 0 i { i j } j ! ( n j ) x j ( 1 + x ) n − j \sum_{i=0}^na_i\sum_{j=0}^{i}\begin{Bmatrix}i\\j\end{Bmatrix}j!\binom{n}{j}x^j(1+x)^{n-j} i=0naij=0i{ij}j!(jn)xj(1+x)nj ∑ i = 0 n a i ∑ j = 0 i { i j } A ( n , j ) × x j ( 1 + x ) n − j \sum_{i=0}^na_i\sum_{j=0}^{i}\begin{Bmatrix}i\\j\end{Bmatrix}A(n,j)\times x^j(1+x)^{n-j} i=0naij=0i{ij}A(n,j)×xj(1+x)nj这个排列数会算吧,快速幂会写吧,斯特林数会求吧
S ( n , m ) = S ( n − 1 , m − 1 ) + m S ( n − 1 , m ) S(n,m)=S(n-1,m-1)+mS(n-1,m) S(n,m)=S(n1,m1)+mS(n1,m)时间复杂度 O ( m 2 log ⁡ ) O(m^2\log) O(m2log)

#include 
#define int long long
const int M = 1005;
int read()
{
    int num=0,flag=1;
    char c;
    while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
    while(c>='0'&&c<='9')num=(num<<3)+(num<<1)+(c^48),c=getchar();
    return num*flag;
}
int n,x,p,m,ans,s[M][M],A[M];
int qkpow(int a,int b)
{
    int r=1;
    while(b>0)
    {
        if(b&1) r=r*a%p;
        a=a*a%p;
        b>>=1;
    }
    return r;
}
signed main()
{
    n=read();x=read();p=read();m=read();
    s[0][0]=A[0]=1;
    for(int i=1;i<=m;i++)
        A[i]=A[i-1]*(n-i+1)%p;
    for(int i=1;i<=m;i++)
        for(int j=1;j<=i;j++)
            s[i][j]=(s[i-1][j-1]+s[i-1][j]*j)%p;
    for(int i=0;i<=m;i++)
    {
        int a=read(),tmp=0;
        for(int j=0;j<=i;j++)
            tmp=(tmp+s[i][j]*qkpow(x,j)%p*qkpow(1+x,n-j)%p*A[j]%p)%p;
        ans=(ans+a*tmp)%p;
    }
    printf("%lld\n",ans);
}

你可能感兴趣的:(斯特林数)