水题大战Vol.3 B. DP搬运工2 计数DP

水题大战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;
}

posted @ 2020-08-13 19:35  liuchanglc  阅读(194)  评论(1编辑  收藏  举报