P8386 [PA2021] Od deski do desk 题解

P8386 [PA2021] Od deski do desk 题解

考虑一个大的序列一定被分成几个区间来删除。朴素的 dp 定义是 dpi,j 表示前 i 个数,最后一个数元素是 j 的方案数。然而这样不仅不好转移,而且设不下状态。不难发现所有值是等价的。考虑这样一个事情:若我们要分出一个新的区间,那么这个区间的前一部分的区间需要同样合法,原序列是一个排列,位置和元素是一一对应的。于是我们只需要知道分割后前面有几个区间合法以及保证区间结尾后的第一个数和新填的数相同。

于是我们设 dpi,j,0/1 表示前 i 位,有 j 个位置使得其位置 wk 满足 [1,wk] 是合法的,当前序列合法与否。

那么 dp 的转移是显然的:

dpi+1,j,1dpi,j,1×jdpi+1,j+1,0dpi,j,1×(mj)dpi+1,j,1dpi,j,0×jdpi+1,j,0dpi,j,0×(mj)

含义就是选择填和这些位置相同的数或是不同的数,选择 j 意味着选择了合法的状态,反之亦然。

代码:

#include <bits/stdc++.h>
#define N 3005
#define int long long
#define mod 1000000007
using namespace std;
int n, m;
int dp[N][N][2];
void add(int &x, int y) {
	x += y;
	if (x > mod)
		x -= mod;
}
signed main() {
	cin >> n >> m;
	dp[0][0][1] = 1;
	for (int i = 0; i < n; i++)
		for (int j = 0; j <= n; j++) {
			add(dp[i + 1][j][1], dp[i][j][1] * j % mod);
			add(dp[i + 1][j + 1][0], dp[i][j][1] * (m - j) % mod);
			add(dp[i + 1][j][1], dp[i][j][0] * j % mod);
			add(dp[i + 1][j][0], dp[i][j][0] * (m - j) % mod);
		}
	int ans = 0;
	for (int i = 0; i <= n; i++)
		add(ans, dp[n][i][1]);
	cout << ans << "\n";
	return 0;
}
posted @   长安19路  阅读(4)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示