把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

P8386 [PA 2021] Od deski do deski 分析

题目概述

给定 \(n,m\),求有多少个长度为 \(n\) 的序列 \(a\) 满足

  • 每个元素在 \([1,m]\)
  • 存在一种删除方式,使得整个序列得以被删除。删除方式:选择一个长度不小于 \(2\) 的区间且这两端数字相同即可删除。

数据范围:\(1\leq n\leq 3000,1\leq m\leq 10^9\)

分析

\(n\)\(m\) 小,考虑从 \(n\) 方面开始。

考虑什么时候是合法的。

首先不难想到其中一种:x.....x

或者是拼起来的:x.....xy.....y

所以也可以是两种组合在一起:x.....xxy.....y.....x

考虑在某一个序列后面加一个数 \(x\)

如果我们在前面找到一个与这个数相同且它前面是合法的那么我当前这个就是合法的。

所以我们需要维护序列长度和可以合法的答案(因为序列中可以有多个合法的前缀)。

故设 \(g_{i,j,0/1}\) 表示现在的长度为 \(i\)\(j\) 个候选的答案,现在组成的序列是否是合法的。

按照上述条件转移即可。

代码

时间复杂度 \(\mathcal{O}(n^2)\)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <stdlib.h>
#include <algorithm>
#include <vector>
#define int long long
#define N 3005
using namespace std;
const int mod = 1e9 + 7;
int n,g[N][N][2],m;
void pls(int &x,int y) {(x += y) %= mod;}
signed main(){
	cin >> n >> m;
	g[1][1][0] = m;
	for (int i = 1;i <= n;i ++)
		for (int j = 1;j <= i;j ++) {
			if (g[i][j][0]) {
				pls(g[i + 1][j][0],g[i][j][0] * (m - j) % mod);
				pls(g[i + 1][j][1],g[i][j][0] * j % mod);
			}
			if (g[i][j][1]) {
				pls(g[i + 1][j][1],g[i][j][1] * j % mod);
				pls(g[i + 1][j + 1][0],g[i][j][1] * (m - j) % mod);
			} 
		}
	int ans = 0;
	for (int i = 1;i <= n;i ++) ans = (ans + g[n][i][1]) % mod;
	cout << ans;
	return 0;
}
posted @ 2026-02-26 21:43  high_skyy  阅读(2)  评论(0)    收藏  举报
浏览器标题切换
浏览器标题切换end