把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【LOJ3600】「PA 2021」Od deski do deski(DP)

题目链接

  • 求有多少个长度为 \(n\) 、值域为 \(1\sim m\) 的序列,满足可以通过若干次删除长度大于 \(1\) 且两端元素相同的区间将其删空。
  • \(1\le n\le3\times10^3\)\(1\le m\le10^9\)

判断合法的条件

考虑如何判断一个序列是否合法。

从左往右扫一遍,维护所有合法的端点值,初始只有第一个数的值。

如果一个位置填了合法的值,则后一个位置的值在填写后也将变成合法的。

只要满足最后一个数填写时是合法的值即可。

动态规划

发现合法的值具体是谁并没有影响,因此只需记录合法的值的个数。

\(f_{i,j}\) 表示填到第 \(i\) 个数一共有 \(j\) 个合法值,且 \(i\) 填写时不是合法值的方案数;\(g_{i,j}\) 表示 \(i\) 填写时是合法值的方案数。

转移比较简单,填了合法值会转移到 \(g\),填了不合法值会转移到 \(f\),且从 \(g\) 转移到 \(f\) 时合法值个数会加 \(1\)

代码:\(O(n^2)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Rg register
#define RI Rg int
#define Cn const
#define CI Cn int&
#define I inline
#define W while
#define N 3000
#define X 1000000007
using namespace std;
int n,m,f[N+5][N+5],g[N+5][N+5];
int main()
{
	RI i,j;for(scanf("%d%d",&n,&m),f[1][1]=m,i=2;i<=n;++i) for(j=1;j<=min(i,m);++j)
		f[i][j]=(1LL*f[i-1][j]*(m-j)+1LL*g[i-1][j-1]*(m-j+1))%X,g[i][j]=1LL*(f[i-1][j]+g[i-1][j])*j%X;//DP转移
	RI t=0;for(j=1;j<=min(n,m);++j) t=(t+g[n][j])%X;return printf("%d\n",t),0;
}
posted @ 2022-04-21 20:56  TheLostWeak  阅读(87)  评论(0编辑  收藏  举报