AGC024E Sequence Growing Hard

Link
\(A_i\)相比于\(A_{i-1}\)只多了一个元素,保证新加入的元素放在一个不大于它的元素的前面即可。
但是这样会算重,让新加入的元素放在一个小于它的元素前面即可去重。
为了方便我们认为\(A_0=\{0\}\)
在第\(i\)次操作中,如果我们把新加入的元素放在第\(j\)次操作加入的元素的左边,那么我们就认为\(j\)\(i\)的父亲。
那么我们可以得到一棵\(n+1\)个点的数,树上的点有\([0,n]\)的标号,且每个点都有一个\([0,k]\)之间的权值,而这棵树满足以标号/权值来看都是一个严格小根堆。
可以发现满足以标号/权值来看都是一个严格小根堆的树与操作方案一一对应。
\(f_{i,j}\)表示有\(i\)个点,标号为\([0,i)\),根节点权值为\(j\)的合法的树的个数。
很显然此时\(0\)为根节点,转移枚举\(1\)号点(标号最小的儿子)的权值与子树大小即可。
\(f_{i,j}=\sum\limits_{v=j+1}^k\sum\limits_{l=1}^{i-1}f_{i-l,j}f_{l,v}{i-2\choose l-1}\)
\(s_{i,j}=\sum\limits_{l=j}^kf_{i,l}\)后缀和优化即可。

#include<cstdio>
const int N=307;
int P,C[N][N],f[N][N],s[N][N];
void inc(int&a,int b){a+=b-P,a+=a>>31&P;}
void dec(int&a,int b){a-=b,a+=a>>31&P;}
int main()
{
    int n,k;scanf("%d%d%d",&n,&k,&P);
    for(int i=0;i<=n;++i) for(int j=C[i][0]=1;j<=i;++j) inc(C[i][j]=C[i-1][j],C[i-1][j-1]);
    for(int i=0;i<=k;++i) f[1][i]=1,s[1][i]=k-i+1;
    for(int i=2;i<=n+1;++i)
    {
	for(int j=0;j<=k;++j) for(int l=1;l<i;++l) inc(f[i][j],1ll*f[i-l][j]*s[l][j+1]%P*C[i-2][l-1]%P);
	for(int j=k;j;--j) inc(s[i][j]=s[i][j+1],f[i][j]);
    }
    printf("%d",f[n+1][0]);
}
posted @ 2020-04-14 17:10  Shiina_Mashiro  阅读(322)  评论(0编辑  收藏  举报