1. 程式人生 > 實用技巧 >水題大戰Vol.3 B. DP搬運工2

水題大戰Vol.3 B. DP搬運工2

水題大戰Vol.3 B. DP搬運工2

題目描述

給你\(n,K\),求有多少個\(1\)\(n\) 的排列,恰好有\(K\)個數\(i\) 滿足\(a_{i-1},a_{i+1}\) 都小於\(a_i\)

輸入格式

一行兩個整數\(n,K\)

輸出格式

一行一個整數\(ans\)表示答案\(mod 998244353\)

樣例

樣例輸入1

4 1

樣例輸出1

16

樣例輸入2

10 3

樣例輸出2

1841152

資料範圍與提示

對於 \(25\%\) 的測試點,\(1 \leq n,K \leq 10\)

對於 \(50\%\) 的測試點,\(1 \leq n,K \leq 100\)

對於 \(100\%\) 的測試點,\(1 \leq n,K \leq 2000\)

保證資料有梯度

分析

一道典型的計數\(DP\)

我們設 \(f[i][j]\) 為考慮完 \(1 - i\) ,有 \(j\) 個位置滿足要求的方案數

對於 \(f[i-1][j]\) 如果我們向序列中插入一個數 \(i\) 那麼會有兩種情況

1、新插入的\(i\)插入到原來滿足要求的\(j\)個位置旁邊或者數列的兩端,此時滿足要求的位置仍然是\(j\)個,方案數為\((j+1)\times 2\)

2、\(i\)插入到原數列的其它位置,此時滿足要求的位置變為 \(j+1\) 個,方案數為 \(i-(j+1) \times 2\)

程式碼

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define mian main
const int maxn=2e3+5;
const int mod=998244353;
int n,k,f[maxn][maxn];
signed mian(){
	scanf("%lld%lld",&n,&k);
	f[1][0]=1,f[2][0]=2;
	for(int i=3;i<=n;i++){
		int m=min(k,i/2);
		for(int j=0;j<=m;j++){
			if(f[i-1][j]==0) continue;
			int now=f[i-1][j];
			int fa=(j+1)*2;
			f[i][j]=(f[i][j]+f[i-1][j]*fa)%mod;
			f[i][j+1]=(f[i][j+1]+f[i-1][j]*(i-fa))%mod;
		}
	}
	printf("%lld\n",f[n][k]);
	return 0;
}