2020CCPC秦皇島H.Holy Sequence
好久沒寫題解了,做了個計數感覺有點妙妙,就來口胡一波。
由于我們要對于每一個\(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}\)。以計算\(\binom{x}{2}\)為例,我們完全可以欽定兩個位置填\(t\),其余位置構成一個合法序列即可算貢獻即可,而注意到我們欽定了\(i\)填\(t\),那么\(i\)之后的位置填\(t\)都是合法的,所以我們其實并不是很關心這兩個位置到底在哪里,在\(i\)后面用組合數隨便欽定兩個位置即可。
最后還需要求出\(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;
}

浙公網安備 33010602011771號