New Year Garland

New Year Garland

Problem - 140E - Codeforces

题意

​ 用 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个圆排列的方案数,按照最后一个放哪即:

  1. 新开一堆, f [ i − 1 ] [ j − 1 ] f[i-1][j-1] f[i1][j1]
  2. 放到前面的某堆,即 f [ i − 1 ] [ j ] × ( i − 1 ) f[i-1][j]\times (i-1) f[i1][j]×(i1) (将一个 x x x插入一个 k k k个数构成圆,由于一共有 k k k个空子,因此有 k k k中方案,由于一共有 i − 1 i-1 i1个空,因此有 i − 1 i-1 i1种方案)

因此 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[i1][j1]+f[i1][j]×(i1)

第二类斯特林数:将 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个集合的方案数,按照最后一个放哪即:

  1. 新开一个集合, f [ i − 1 ] [ j − 1 ] f[i-1][j-1] f[i1][j1]
  2. 放到前面的某个集合,即 f [ i − 1 ] [ j ] × j f[i-1][j]\times j f[i1][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[i1][j1]+f[i1][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[i1][j]×(j1)+f[i1][j1]

​ 之后考虑 d p [ i ] [ j ] dp[i][j] dp[i][j]:表示 [ 1 , i − 1 ] [1,i-1] [1,i1]行均合法的情况下,第 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=1li1dp[i1][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[i1][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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值