2020CCPC秦皇島H.Holy Sequence
阿新 • • 發佈:2021-10-30
好久沒寫題解了,做了個計數感覺有點妙妙,就來口胡一波。
由於我們要對於每一個\(t\)單獨計算答案,所以必然要先列舉\(t\),考慮一個\(n\)-holy序列的字首最大值每次最多隻能增加\(1\),必然可以化為\(1......2......3......4......5.......t\)的形式。我們列舉\(t\)第一次出現的位置為\(i\),\(i\)前面的方案數可以用一個dp預處理出來。觀察這個dp的轉移發現其實就是第二類斯特林數
這樣\(t\)出現的位置必然在\(i\)以及\(i\)之後,考慮一個經典的恆等式\(x^2=2\times \binom{x}{2}+\binom{x}{1}\)
最後還需要求出\(i\)填\(t\)後面的合法方案數,發現從\(i\)填到\(n\)需要強行從\(t\)開始難以處理,於是考慮從\(n\)倒著向\(i\)搞一個dp,這樣數是從大到小的,並不需要強行令\(t\)作為開頭。
預處理第二類斯特林數和這個反向的\(dp\),就能\(O(n)\)回答一個\(t\)
程式碼
#include<bits/stdc++.h> #define re signed const int maxn=3005; int n,mod,g[maxn][maxn],h[maxn][maxn]; inline int qm(int x) {return x>=mod?x-mod:x;} inline int calc(int x) {return (1ll*x*(x-1)/2ll)%mod;} int main() { int Test;scanf("%d",&Test); for(re int te=1;te<=Test;++te) { scanf("%d%d",&n,&mod); for(re int i=0;i<=n+1;++i) for(re int j=0;j<=n+1;++j)g[i][j]=h[i][j]=0; g[0][0]=1; for(re int i=0;i<n;i++) for(re int j=0;j<=i;++j) { if(!g[i][j]) continue; g[i+1][j+1]=qm(g[i+1][j+1]+g[i][j]); g[i+1][j]=qm(g[i+1][j]+1ll*g[i][j]*j%mod); } for(re int i=1;i<=n;i++)h[0][i]=1; for(re int i=0;i<n;i++) for(re int j=n;j;--j) { if(!h[i][j]) continue; h[i+1][j]=qm(h[i+1][j]+1ll*h[i][j]*j%mod); if(j-1>0) h[i+1][j-1]=qm(h[i+1][j-1]+h[i][j]); } printf("Case #%d:\n",te); for(re int t=1;t<=n;++t) { int ans=0; for(re int i=1;i<=n;i++) { if(!g[i-1][t-1]) continue; int tot=h[n-i][t]; if(n-i-1>=0) tot=qm(tot+3ll*h[n-i-1][t]%mod*(n-i)%mod); if(n-i-2>=0) tot=qm(tot+2ll*h[n-i-2][t]%mod*calc(n-i)%mod); ans=qm(ans+1ll*tot*g[i-1][t-1]%mod); } printf("%d ",ans); } puts(""); } return 0; }