P8699

排列数

题目描述

在一个排列中,一个折点是指排列中的一个元素,它同时小于两边的元素,或者同时大于两边的元素。

对于一个 \(1 ∼ n\) 的排列,如果可以将这个排列中包含 \(t\) 个折点,则它称为一个 \(t + 1\) 单调排列。

例如,排列 \((1, 4, 2, 3)\) 是一个 \(3\) 单调排列,其中 \(4\)\(2\) 都是折点。

给定 \(n\)\(k\),请问 \(1 ∼ n\) 的所有排列中有多少个 \(k\) 单调排列?

输入格式

输入一行包含两个整数 \(n\), \(k\)

输出格式

输出一个整数,表示答案。答案可能很大,你可需要输出满足条件的排列
数量除以 \(123456\) 的余数即可。

样例 #1

样例输入 #1

4 2

样例输出 #1

12

提示

对于 \(20 \%\) 的评测用例, \(1 \leq k \leq n \leq 10\);

对于 \(40 \%\) 的评测用例, \(1 \leq k \leq n \leq 20\); 对于 \(60 \%\) 的评测用例, \(1 \leq k \leq n \leq 100\);

对于所有评测用例, \(1 \leq k \leq n \leq 500\)

又是什么神仙DP啊!!!

证明:https://www.acwing.com/solution/content/28097/
神仙状态转移……

主要是不能只考虑折点及左右 还要考虑 左左 右右 共计五个点的关系

然后分别分为 +0 +1 +2 来分类讨论

f[i][j] = f[i - 1][j] * j + f[i - 1][j - 1] * 2 + f[i - 1][j - 2] * (i - j)

Code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=123456;
int n,k;
int f[505][505];
signed main()
{
	ios::sync_with_stdio(false);
	cin>>n>>k;
	f[1][1]=1;
	f[2][1]=2;
	for(int i=3;i<=n;i++)
		for(int j=1;j<=min(i,k);j++)
		{
			f[i][j]=((f[i][j]+f[i-1][j]*j+f[i-1][j-1]*2)%mod+mod)%mod;
			if(j>=2)
				f[i][j]=((f[i][j]+f[i-1][j-2]*(i-j))%mod+mod)%mod;
		}
	cout<<(f[n][k]%mod+mod)%mod<<'\n';
	return 0;
}
posted @ 2023-03-25 11:11  N0zoM1z0  阅读(18)  评论(0编辑  收藏  举报