【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;
}
待到再迷茫时回头望,所有脚印会发出光芒