BZOJ 2655 calc (组合计数、DP、多项式、拉格朗日插值)
生活随笔
收集整理的這篇文章主要介紹了
BZOJ 2655 calc (组合计数、DP、多项式、拉格朗日插值)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
題目鏈接
https://www.lydsy.com/JudgeOnline/problem.php?id=2655
題解
據說有一種神仙容斥做法,但我不會。
以及貌似網上大多數人的dp和我的做法都不一樣。
下面講我的做法:
首先由于元素互不相同,那么顯然可以先不考慮順序。
所以要求的就是\(n![x^n]\prod^{m}_{i=1}(1+ix)\) (直接莽上生成函數是不是有點……)
于是發現這個東西和第一類斯特林數生成函數幾乎一樣,也可以輕易寫出遞推式\(dp[i][j]=dp[i-1][j]+dp[i-1][j-1]\times i\)
有一個結論是,\(dp[i][j]\)是關于\(i\)的不超過\(2j\)次多項式。
感性理解的話,就是從\(1\)到\(i\)里選\(j\)個,求乘積之和,\(1\)到\(i\)里選\(j\)個一共有\(i\choose j\)種選法,這顯然是\(j\)次多項式,再求\(j\)個不超過\(i\)的數的乘積顯然也是\(j\)次,那么總共就是\(2j\)次。
于是求出前\(2n\)項,Lagrange插值計算即可。
(所以這其實是一種求第一類斯特林數\(\begin{bmatrix}n\\m\end{bmatrix}\) (\(n-m\)較小)的新方法?)
時間復雜度\(O(n^2)\).
代碼
#include<cstdio> #include<cstdlib> #include<cstring> #include<cassert> #include<iostream> #define llong long long using namespace std;const int N = 1000; llong fact[N+3],finv[N+3]; llong dp[N+3][N+3]; llong n,m,P;llong quickpow(llong x,llong y) {llong cur = x,ret = 1ll;for(int i=0; y; i++){if(y&(1ll<<i)) {y-=(1ll<<i); ret = ret*cur%P;}cur = cur*cur%P;}return ret; } llong mulinv(llong x) {return quickpow(x,P-2);}namespace Lagrange {llong ax[N+3],ay[N+3],poly[N+3];llong aux[N+3],aux2[N+3];void lagrange(int n){aux[0] = 1ll;for(int i=0; i<=n; i++){for(int j=i+1; j>0; j--){aux[j] = (aux[j-1]-aux[j]*ax[i]%P+P)%P;}aux[0] = P-aux[0]*ax[i]%P;}for(int i=0; i<=n; i++){llong coe = 1ll;for(int j=0; j<=n; j++){if(i==j) continue;coe = coe*(ax[i]-ax[j]+P)%P;}coe = mulinv(coe);for(int j=0; j<=n+1; j++) aux2[j] = aux[j];for(int j=n; j>=0; j--){poly[j] = (poly[j]+ay[i]*aux2[j+1]%P*coe)%P;aux2[j] = (aux2[j]+aux2[j+1]*ax[i])%P;}}}llong calc(int n,llong x){llong ret = 0ll;for(int i=n; i>=0; i--){ret = (ret*x+poly[i])%P;}return ret;}void clear(int n){for(int i=0; i<=n+1; i++) aux[i] = aux2[i] = poly[i] = 0ll;} }int main() {scanf("%lld%lld%lld",&m,&n,&P);fact[0] = 1ll; for(int i=1; i<=N; i++) fact[i] = fact[i-1]*i%P;finv[N] = quickpow(fact[N],P-2); for(int i=N-1; i>=0; i--) finv[i] = finv[i+1]*(i+1)%P;dp[0][0] = 1ll;for(int i=1; i<=n+n; i++){dp[i][0] = 1ll;for(int j=1; j<=i; j++){dp[i][j] = (dp[i-1][j]+dp[i-1][j-1]*i)%P;}}for(int i=0; i<=n+n; i++){Lagrange::ax[i] = i;Lagrange::ay[i] = dp[i][n];}Lagrange::lagrange(n+n);llong ans = Lagrange::calc(n+n,m)*fact[n]%P;printf("%lld\n",ans);return 0; }總結
以上是生活随笔為你收集整理的BZOJ 2655 calc (组合计数、DP、多项式、拉格朗日插值)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: POJ 1430 Binary Stir
- 下一篇: BZOJ 1488 Luogu P472