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}=sumlimits_{v=j+1}^ksumlimits_{l=1}^{i-1}f_{i-l,j}f_{l,v}{i-2choose l-1})
(s_{i,j}=sumlimits_{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]);
}
原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/12699273.html