New Year Garland
题意
用 m m m种颜色的彩球装点 n n n层的圣诞树。圣诞树的第 i i i层恰由 l i l_i li个彩球串成一行,且同一层内的相邻彩球颜色不同,同时相邻两层所使用彩球的颜色集合不同。求有多少种装点方案,答案对 p p p取模。
思路
- 组合数, d p dp dp
第一类斯特利数:将 n n n个数分成 k k k 个圆排列的方案数。
从前往后 d p dp dp,设 f [ i ] [ j ] f[i][j] f[i][j]:将 i i i个数分成 j j j个圆排列的方案数,按照最后一个放哪即:
- 新开一堆, f [ i − 1 ] [ j − 1 ] f[i-1][j-1] f[i−1][j−1]
- 放到前面的某堆,即 f [ i − 1 ] [ j ] × ( i − 1 ) f[i-1][j]\times (i-1) f[i−1][j]×(i−1) (将一个 x x x插入一个 k k k个数构成圆,由于一共有 k k k个空子,因此有 k k k中方案,由于一共有 i − 1 i-1 i−1个空,因此有 i − 1 i-1 i−1种方案)
因此 f [ i ] [ j ] = f [ i − 1 ] [ j − 1 ] + f [ i − 1 ] [ j ] × ( i − 1 ) f[i][j]=f[i-1][j-1]+f[i-1][j]\times (i-1) f[i][j]=f[i−1][j−1]+f[i−1][j]×(i−1)
第二类斯特林数:将 n n n个数分成 k k k个集合的方案数。
从前往后 d p dp dp,设 f [ i ] [ j ] f[i][j] f[i][j]:将 i i i个数分成 j j j个集合的方案数,按照最后一个放哪即:
- 新开一个集合, f [ i − 1 ] [ j − 1 ] f[i-1][j-1] f[i−1][j−1]
- 放到前面的某个集合,即 f [ i − 1 ] [ j ] × j f[i-1][j]\times j f[i−1][j]×j
因此 f [ i ] [ j ] = f [ i − 1 ] [ j − 1 ] + f [ i − 1 ] [ j ] × j f[i][j]=f[i-1][j-1]+f[i-1][j]\times j f[i][j]=f[i−1][j−1]+f[i−1][j]×j
首先考虑 f [ i ] [ j ] f[i][j] f[i][j]:第 i i i行放 j j j 种小球的方案数,类似于第二类斯特林数的求法得, f [ i ] [ j ] = f [ i − 1 ] [ j ] × ( j − 1 ) + f [ i − 1 ] [ j − 1 ] f[i][j]=f[i-1][j]\times(j-1)+f[i-1][j-1] f[i][j]=f[i−1][j]×(j−1)+f[i−1][j−1]。
之后考虑 d p [ i ] [ j ] dp[i][j] dp[i][j]:表示 [ 1 , i − 1 ] [1,i-1] [1,i−1]行均合法的情况下,第 i i i行放 j j j种小球的方案数,我们依次枚举 j ∈ [ 1 , l i ] j\in [1,l_i] j∈[1,li]即可,即可求出 [ 1 , i ] [1,i] [1,i]行均合法的方案数是多少。
由于题目的限制,我们考虑容斥来做,即对于 j ∈ [ 1 , l i ] j\in[1,l_i] j∈[1,li]来说先求总的再减去不合法的即可,设 s u m = ∑ j = 1 l i − 1 d p [ i − 1 ] [ j ] sum=\displaystyle\sum_{j=1}^{l_{i-1}}dp[i-1][j] sum=j=1∑li−1dp[i−1][j]即对于 求总的方案 d p [ i ] [ j ] = s u m × f [ l i ] [ j ] × A l i j dp[i][j]=sum\times f[l_i][j]\times A_{l_i}^j dp[i][j]=sum×f[li][j]×Alij,对于不合法的方案即 d p [ i − 1 ] [ j ] × j ! × f [ l i ] [ j ] dp[i-1][j]\times j!\times f[l_i][j] dp[i−1][j]×j!×f[li][j]。
每一种不同的性质,要不同的探讨
Code
#include <bits/stdc++.h>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k, p;
int a[N], l[N * 10];
LL dp[2][5500];
int f[5510][5510];
int fact[N], rfact[N];
int main() {
ios::sync_with_stdio(false), cin.tie(0);
cin >> n >> m >> p;
for (int i = 1; i <= n; i ++) cin >> l[i];
f[0][0] = 1;
for (int i = 1; i <= 5000; i ++)
for (int j = 1; j <= i; j ++)
f[i][j] = ((LL)f[i - 1][j - 1] + (LL)f[i - 1][j] * (j - 1)) % p;
fact[0] = rfact[0] = 1;
for (int i = 1; i <= 5050; i ++) {
fact[i] = (LL)fact[i - 1] * i % p;
rfact[i] = (LL)rfact[i - 1] * (m - i + 1) % p;
}
LL sum = 1, res = 0;
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= l[i]; j ++) {
dp[i&1][j] = (LL)sum * rfact[j] % p * f[l[i]][j] % p;
if (i > 1 && j <= l[i - 1])
dp[i&1][j] = ((LL)dp[i&1][j] - (LL)dp[(i-1)&1][j] * fact[j] % p * f[l[i]][j] % p + p) % p;
res += dp[i&1][j];
res %= p;
}
sum = res;
res = 0;
}
cout << sum << '\n';
return 0;
}