1. 程式人生 > 實用技巧 >【洛谷6620】[省選聯考 2020 A 卷] 組合數問題(下降冪)

【洛谷6620】[省選聯考 2020 A 卷] 組合數問題(下降冪)

點此看題面

大致題意: 給定\(n,x\)和一個\(m\)次多項式\(f(k)\),求\(\sum_{k=0}^nf(k)\times x^k\times C_n^k\)

下降冪

首先不難想到要把\(f(k)\)拆開。

然而,這道題妙就妙在,它的第一步是把\(f(k)\)轉化為下降冪形式,即令:

\[f(k)=\sum_{i=0}^ma_ik^i=\sum_{i=0}^mb_ik^{\underline{i}} \]

由於此處\(m\le1000\),因此我們只要\(O(m^2)\)暴力轉就可以了。

關於第二類斯特林數與下降冪有一個公式:

\[x^n=\sum_{i=0}^nS2(n,i)\times x^{\underline{i}} \]

(根據式子本身意義理解,左式\(x^n\)就是假設所有盒子本質不同的方案數,右式就是列舉非空盒子個數求出對應方案數)

所以可以得到:

\[f(k)=\sum_{i=0}^ma_ik^i=\sum_{i=0}^ma_i\sum_{j=0}^iS2(i,j)k^{\underline{j}}=\sum_{i=0}^m(\sum_{j=i}^mS2(j,i)\times a_j)k^{\underline{i}} \]

即:

\[b_i=\sum_{j=i}^mS2(j,i)\times a_j \]

推式子

好了,現在我們已經把\(f(k)\)轉化成了下降冪形式,接著就是把它代入:

\[\sum_{k=0}^n\sum_{i=0}^mb_ik^{\underline{i}}\times x^k\times C_n^k \]

顯然把\(i\)的列舉調到前面去得到:

\[\sum_{i=0}^mb_i\sum_{k=0}^nk^{\underline{i}}\times x^k\times C_n^k \]

接下來介紹一個新的公式:

\[k^{\underline{i}}\times C_n^k=n^{\underline{i}}\times C_{n-i}^{k-i} \]

證明就是暴拆:

\[k^{\underline{i}}\times C_n^k=\frac{k!}{(k-i)!}\times \frac{n!}{k!(n-k)!}=\frac{n!}{(k-i)!(n-k)!} \]

\[n^{\underline{i}}\times C_{n-i}^{k-i}=\frac{n!}{(n-i)!}\times \frac{(n-i)!}{(k-i)!(n-k)!}=\frac{n!}{(k-i)!(n-k)!} \]

套用這個公式得到:

\[\sum_{i=0}^mb_in^{\underline{i}}\sum_{k=0}^n C_{n-i}^{k-i}\times x^k \]

由於\(k-i<0\)時組合數沒用,因此索性令\(k\)列舉\(k-i\),得到:

\[\sum_{i=0}^mb_in^{\underline{i}}\sum_{k=0}^{n-i} C_{n-i}^k\times x^{k+i} \]

\(x^{k+i}\)中提出一個\(x_i\)

\[\sum_{i=0}^mb_in^{\underline{i}}x^i\sum_{k=0}^{n-i} C_{n-i}^k\times x^{k} \]

然後後面這傢伙顯然就是一個二項式定理啊:

\[\sum_{i=0}^mb_in^{\underline{i}}x^i(x+1)^{n-i} \]

於是就做完了,這個式子可以直接暴力算。

程式碼

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define M 1000
using namespace std;
int n,m,x,X,a[M+5],b[M+5],Fac[M+5],S2[M+5][M+5];
I int QP(RI x,RI y) {RI t=1;W(y) y&1&&(t=1LL*t*x%X),x=1LL*x*x%X,y>>=1;return t;}
int main()
{
	RI i,j;scanf("%d%d%d%d",&n,&x,&X,&m);
	for(S2[0][0]=i=1;i<=m;++i) for(S2[i][0]=0,j=1;j<=i;++j) S2[i][j]=(1LL*S2[i-1][j]*j+S2[i-1][j-1])%X;//預處理第二類斯特林數
	for(i=0;i<=m;++i) scanf("%d",a+i);for(i=0;i<=m;++i) for(j=i;j<=m;++j) b[i]=(1LL*S2[j][i]*a[j]+b[i])%X;//多項式轉下降冪
	RI t=0,n_=1;for(i=0;i<=m;n_=1LL*n_*(n-i)%X,++i) t=(1LL*b[i]*n_%X*QP(x,i)%X*QP(x+1,n-i)+t)%X;//計算答案,n_記錄n的i次下降冪
	return printf("%d\n",t),0;//輸出答案
}