好久没写题解了,做了个计数感觉有点妙妙,就来口胡一波。
由于我们要对于每一个(t)单独计算答案,所以必然要先枚举(t),考虑一个(n)-holy序列的前缀最大值每次最多只能增加(1),必然可以化为(1......2......3......4......5.......t)的形式。我们枚举(t)第一次出现的位置为(i),(i)前面的方案数可以用一个dp预处理出来。观察这个dp的转移发现其实就是第二类斯特林数
这样(t)出现的位置必然在(i)以及(i)之后,考虑一个经典的恒等式(x^2=2 imes inom{x}{2}+inom{x}{1})。以计算(inom{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:
",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;
}