题意
有个圣诞树有层,每一层有
个彩灯, 让你用
种颜色给彩灯上色,要求圣诞树需要满足下列条件
- 每一层相邻两个彩灯的颜色不一样
- 相邻两层使用的颜色集合不一样
思路
假设我们就一层的话,我们可以用动态规划计算方案数
我们假设表示前
个彩灯按顺序用了第
种到第
种颜色的方案数,转移方程如下
接着我们设表示前
层,且第
层按顺序用了第
种到第
种颜色的方案数,同时设
表示前
层的合法方案数
我们考虑(第一层比较简单), 假设没有第二个约束的话,
但是因为有了第二个约束,就变成了
那么我们就可以计算
代码
/**
* author: andif
* created: 31.08.2023 23:15:23
**/
#include<bits/stdc++.h>
using namespace std;
#define de(x) cerr << #x << " = " << x << endl
#define dd(x) cerr << #x << " = " << x << " "
#define rep(i, a, b) for(int i = a; i < b; ++i)
#define per(i, a, b) for(int i = a; i > b; --i)
#define mt(a, b) memset(a, b, sizeof(a))
#define sz(a) (int)a.size()
#define fi first
#define se second
#define qc ios_base::sync_with_stdio(0);cin.tie(0)
#define eb emplace_back
#define all(a) a.begin(), a.end()
using ll = long long;
using db = double;
using pii = pair<int, int>;
using pdd = pair<db, db>;
using pll = pair<ll, ll>;
using vi = vector<int>;
const db eps = 1e-9;
const int N = 5006;
// int f[2][N][N], g[2][N];
int main() {
qc;
int n, m, p;
cin >> n >> m >> p;
vi l(n);
rep(i, 0, n) cin >> l[i];
vector<vi> f(N, vi(N, 0));
vector<vi> g(2, vi(N, 0));
f[0][0] = 1;
rep(i, 1, N) {
rep(j, 1, i + 1) {
f[i][j] = (1ll * f[i - 1][j] * (j - 1) % p + f[i - 1][j - 1]) % p;
// dd(i), dd(j), de(f[0][i][j]);
}
}
rep(j, 1, min(l[0], m) + 1) g[0][j] = f[l[0]][j];
vi F(N);
F[0] = 1;
rep(i, 1, N) {
F[i] = 1ll * F[i - 1] * i % p;
// dd(i), de(F[i]);
}
vi t(m + 1);
t[0] = 1;
rep(i, 1, m + 1) {
t[i] = (1ll * t[i - 1] * (m - i + 1)) % p;
// dd(i), de(t[i]);
}
vi s(n);
rep(j, 1, min(l[0], m) + 1) {
s[0] = (s[0] + 1ll * g[0][j] * t[j] % p) % p;
}
// de(s[0]);
rep(k, 1, n) {
int c = k & 1;
rep(j, 1, min(l[k], m) + 1) {
int cur = g[!c][j];
if (j > l[k - 1]) cur = 0;
g[c][j] = 1ll * f[l[k]][j] * (s[k - 1] - 1ll * cur * F[j] % p) % p;
if (g[c][j] < 0) g[c][j] += p;
// dd(k), dd(j), de(g[c][j]);
}
rep(j, 1, min(l[k], m) + 1) {
s[k] = (s[k] + 1ll * g[c][j] * t[j] % p) % p;
}
if (s[k] < 0) s[k] += p;
// g[!c] = g[c];
}
cout << s[n - 1] << '\n';
return 0;
}