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