1. 程式人生 > 其它 >【LOJ3600】「PA 2021」Od deski do deski(DP)

【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;
}