AGC024E Sequence Growing Hard

题意

给出\(n\),\(m\),\(mu\),问有多少个序列组\((A_0,A_1,\dots,A_n)\)满足:

  • 序列\(Ai\)的长度恰好为\(i\)
  • 所有元素均在\([1,m]\)
  • \(A_{i−1}\)\(A_i\)的子序列
  • \(A_i\)的字典序大于\(A_{i−1}\)

答案模\(mu\)输出。
\(n,k \le 300\)
传送门

思路

又是一道神仙\(dp\)
一个很重要的思路:把数从小往大插入
当我们插入\(i\)时,因为数列中的数都是\(\le i\)的,所以\(i\)插在所有位置都是可以的
例如:\(1323\),考虑插入\(3\)
最前面:\(31323\);一:\(13323\);二:\(13323\);三:\(13233\);四:\(13233\)
不过同时我们也发现:会算重。而且是当插到\(i\)前面的时候
所以我们强行规定相同数一定要插在后面就可以了。
我们记录\(dp[i][j][k]\)表示当前进行到第\(i\)个操作,放到数字\(j\),有\(k\)个数后可以放(注意这意味着有\(k+1\)种,因为开头也是可以放的)。
转移:

  • \(dp[i][j][k - 1]+= dp[i][j][k] (k>0)\)表示这个位置的数后不放
  • \(dp[i][j + 1][i] += dp[i][j][k] (k=0)\)\(j\)已经不能放了,从\(j+1\)新开始放(不存在相同的,所以所有数后都能放)
  • \(dp[i + 1][j][k] += dp[i][j][k]*(k + 1)\) 表示我们放置这个数,放这个数有\(k+1\)中选择。

代码十分简短
参考

#include <bits/stdc++.h>
#define upd(x,y) x=(x+y>=mu?x+y-mu:x+y)
int n,m,mu,dp[305][305][305];
int main(){
	scanf("%d%d%d",&n,&m,&mu);
	dp[0][1][0]=1;
	for (int i=0;i<=n;i++)
		for (int j=1;j<=m;j++)
			for (int k=i;k>=0;k--){
				if (k) upd(dp[i][j][k-1],dp[i][j][k]);
				else upd(dp[i][j+1][i],dp[i][j][k]);
				upd(dp[i+1][j][k],1ll*dp[i][j][k]*(k+1)%mu);
			} 
	printf("%d",dp[n][m][0]);
}
posted @ 2019-11-01 17:44  flyfeather  阅读(595)  评论(0编辑  收藏  举报