题解 AT3962 【[AGC024E] Sequence Growing Hard】
UPD 2021.3.20:修复了式子中的一个小 typ
题目传送门
楼下大佬用的是三维
题目可以转化为每次在序列中插入一个
显然相同的数可以合并,因为在由相同的数
再考虑字典序的问题。你只能序列末尾或者一个
但这样问题还是比较棘手,我们还需进一步转化。
我们把操作序列转化为一棵有根树,树上每个节点都是一个二元组
由于我们只能在
不妨举个例子,假设有如下的操作序列:
- 向序列中插入数
1 ,得到序列[1] 。这可看成将点(1,1) 挂在点(0,0) 下面。 - 在
1 前插入3 ,得到序列[3,1] 。这可看成将点(3,2) 挂在点(1,1) 下面。 - 在序列末尾插入
2 ,得到序列[3,1,2] 。这可看成将点(2,3) 挂在点(0,0) 下面。 - 在
1 再插入一个3 ,得到序列[3,3,1,2] 。这可看成将点(3,4) 挂在点(1,1) 下面。 - 在
1 前插入一个2 ,得到序列[3,3,2,1,2] 。这可看成将点(2,5) 挂在点(1,1) 下面。 - 在第二个
3 前插入一个4 ,得到序列[3,4,3,2,1,2] 。这可看成将点(4,6) 挂在点(3,4) 下面。
这样
一种操作序列恰对应一棵树,一棵满足条件的树也对应一种操作序列。因此问题转化为有多少个满足条件的树。
这就可以直接
考虑转移,对于
后面那个
/*
Contest: -
Problem: Atcoder Grand Contest 024 E
Author: tzc_wk
Time: 2020.7.22
*/
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fz(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define foreach(it,v) for(__typeof(v.begin()) it=v.begin();it!=v.end();it++)
#define all(a) a.begin(),a.end()
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,0x3f,sizeof(a))
#define fillsmall(a) memset(a,0xcf,sizeof(a))
#define y1 y1010101010101
#define y0 y0101010101010
#define int long long
typedef pair<int,int> pii;
inline int read(){
int x=0,neg=1;char c=getchar();
while(!isdigit(c)){
if(c=='-') neg=-1;
c=getchar();
}
while(isdigit(c)) x=x*10+c-'0',c=getchar();
return x*neg;
}
int n=read(),k=read(),m=read();
int C[305][305],s[305][305],dp[305][305];
signed main(){
fz(i,0,300){
C[i][0]=1;
fz(j,1,i) C[i][j]=(C[i-1][j]+C[i-1][j-1])%m;
}
// printf("%d\n",C[5][3]);
fz(i,0,k) dp[1][i]=1;
fd(i,k,0) s[1][i]=(s[1][i+1]+dp[1][i])%m;
fz(i,2,n+1){
fz(j,0,k)
fz(l,1,i-1)
dp[i][j]=(dp[i][j]+C[i-2][l-1]*dp[i-l][j]%m*s[l][j+1]%m)%m;
fd(j,k,0) s[i][j]=(s[i][j+1]+dp[i][j])%m;
}
printf("%lld\n",dp[n+1][0]);
return 0;
}