题意:
给出n,k,m,问有多少个序列组
满足以下条件:
序列
的长度恰好为i
所有元素均在
的范围内
是
的子序列
的字典序大于
答案模m输出。
Solution:
我们考虑在一个长为n的序列中加入一个数x,使得新的序列的字典序要比之前的大
那么这个x只能加在某个比x小的数y的左边或者是序列末尾(如果y前有多个等于x的值,那么x加在哪里都一样)
那么我们可以把一个序列组转换成一棵树:
1.每个点有两个信息:标号和权值。
2.初始时有一个节点,标号和权值均为0。
3.当我们通过
构造
时,设在某个数y左边插入了x,新建一个点,标号为i,权值为x。设y的标号为p,那么i的父亲是p
这样就可以把一个序列组和一棵树对应起来
答案即为符合上面条件的树的数量
表示当前的树有i个节点,根的权值为j,所能产生的合法的树的个数
转移时枚举加一棵大小为siz的子树连接根节点:
组合数的意义是:固定了根节点和子树的根节点,我们需要在剩下的i-2个数中选择siz-1个当做子树
加上前缀和优化,复杂度为
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,k,mod;
int C[310][310],f[310][310],s[310][310];
int main()
{
scanf("%d%d%d",&n,&k,&mod);
C[0][0]=1;
for (int i=1;i<=n;i++)
{
C[0][i]=1;
for (int j=1;j<=i;j++)
C[j][i]=(C[j-1][i-1]+C[j][i-1])%mod;
}
for (int i=0;i<=k;i++) f[1][i]=1,s[1][i]=i;
for (int i=2;i<=n+1;i++)
for (int j=0;j<=k;j++)
{
int ans=0;
for (int siz=1;siz<=i-1;siz++)
ans=(1ll*C[siz-1][i-2]*f[i-siz][j]%mod*(s[siz][k]-s[siz][j]+mod)%mod+ans)%mod;
f[i][j]=ans;
s[i][j]=ans;
if (j) s[i][j]=(ans+s[i][j-1])%mod;
}
printf("%d",f[n+1][0]);
}