【CF 140E】New Year Garland(第二类斯特林(Stirling)数+DP+容斥)

【CF 140E】New Year Garland(第二类斯特林(Stirling)数+DP+容斥)

E. New Year Garland
time limit per test
5 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

As Gerald, Alexander, Sergey and Gennady are already busy with the usual New Year chores, Edward hastily decorates the New Year Tree. And any decent New Year Tree must be decorated with a good garland. Edward has lamps of m colors and he wants to make a garland from them. That garland should represent a sequence whose length equals L. Edward’s tree is n layers high and Edward plans to hang the garland so as to decorate the first layer with the first l1 lamps, the second layer — with the next l2 lamps and so on. The last n-th layer should be decorated with the last ln lamps,

Edward adores all sorts of math puzzles, so he suddenly wondered: how many different ways to assemble the garland are there given that the both following two conditions are met:

  1. Any two lamps that follow consecutively in the same layer should have different colors.
  2. The sets of used colors in every two neighbouring layers must be different. We consider unordered sets (not multisets), where every color occurs no more than once. So the number of lamps of particular color does not matter.

Help Edward find the answer to this nagging problem or else he won’t manage to decorate the Tree by New Year. You may consider that Edward has an unlimited number of lamps of each of m colors and it is not obligatory to use all m colors. The garlands are considered different if they differ in at least one position when represented as sequences. Calculate the answer modulo p.

Input

The first line contains three integers n, m and p (1 ≤ n, m ≤ 106, 2 ≤ p ≤ 109) which are the number of the tree’s layers, the number of the lamps’ colors and module correspondingly. The next line contains n integers li (1 ≤ li ≤ 5000, ).

Output

Print the only integer — the number of garlands modulo p.

Examples
Input
3 2 1000
3 1 2
Output
8
Input
2 3 1000
2 2
Output
24
Input
1 1 1000
5
Output
0
Note

In the first sample the following variants are possible: 121|1|12, 121|1|21, 121|2|12, 121|2|21, 212|1|12, 212|1|21, 212|2|12, 212|2|21. In the second sample the following variants are possible: 12|13, 12|23, 12|31, 12|32 and so on.

Figure for the first sample:

题目大意:
圣诞树上挂彩球。
要求从上到下挂n行彩球。
已知有m种颜色的球,球的数量不限。
要求结果对p取模。

之后n个数,表示第 i 根绳长li,也就是要挂 li 个球。
要求每根绳上相邻彩球颜色不同。
相邻的绳子上挂的彩球种类不能相同。

彩球种类不相同是指至少有一种颜色 相邻两根绳子不共用。

考虑 dp[i][j] 表示在第 1 i1根绳子排列合法的情况下,第 i 根绳子用j种小球的合法方案数。

那么 dp[i][j]=k=1mdp[i1][k](ij)(ii1)

这里用容斥是因为求和那一步可以直接在遍历 i1 的过程中得到。
同时注意减去的是用同种小球,而不是同数量。减去的是 i i1用完全相同的小球的方案数。

首先解决i绳子上放j种小球

其实求的是 a[i][j] —长为i的绳子上放j种小球的方案数(两两不相邻)

这里我们让 a[i][j]Ajk 为上述结果(k为可选颜色数量)

为什么要这样搞?因为这样这个数组在容斥那里也就可以用上了。
i与i-1绳子用同样小球的方案数 其实就是 dp[i1][j]a[i][j]Ajj
因为此时 i1

那么a数组怎么搞?其实就是第二类斯特林数。很耳熟吧?或者你已经知道怎么做了,。

不过是种变形。
a[i][j]=dp[i1][j](j1)+dp[i1][j1]

考虑的其实就是j种小球往i个无编号的盒子里放,每个盒子放一个,相邻盒子小球不一样。

dp[i1][j1] 表示 i1 个盒子放 j1 种小球,变成i盒子j小球,自然新增一小球一盒子并相匹配。
此外 dp[i1][j] 表示 i1 个盒子 j 种小球,新增一个盒子时可以放除了相邻盒子中的小球外任意小球,即(j1)个。

然后 Ajm Ajj 预处理出来就行。

搞这么麻烦其实就是为了省去除法。因为可能对任何数取模,逆元不好弄。

代码如下:

#include <iostream>
#include <cmath>
#include <vector>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <queue>
#include <stack>
#include <list>
#include <algorithm>
#include <map>
#include <set>
#define ll long long
#define pr pair<int,int>
#define fread(ch) freopen(ch,"r",stdin)
#define fwrite(ch) freopen(ch,"w",stdout)

using namespace std;
const int inf = 0x3f3f3f3f;
const int msz = 10000;
const double eps = 1e-8;
int mod;

ll xx[5555],c[5555];
ll a[5555][5555];
ll dp[2][5555];

void init(int m)
{
    a[0][0] = 1;
    for(int i = 1; i <= 5000; ++i)
        for(int j = 1; j <= min(m,i); ++j)
        {
            a[i][j] = ((((a[i-1][j]*(j-1))%mod)+a[i-1][j-1])%mod);
        }

    xx[1] = m;
    for(int i = 2; i <= min(m,5000); ++i)
        xx[i] = (xx[i-1]*(m-i+1))%mod;

    c[1] = 1;
    for(int i = 2; i <= min(m,5000); ++i)
        c[i] = (c[i-1]*i)%mod;
}

int main()
{
    //fread("");
    //fwrite("");

    int n,m;

    scanf("%d%d%d",&n,&m,&mod);

    init(m);

    int l;

    int pos = 0;
    ll sum,tmp;
    for(int s = 0; s < n; ++s)
    {
        scanf("%d",&l);

        tmp = 0;
        memset(dp[pos^1],0,sizeof(dp[pos^1]));
        if(s == 0)
        {
            for(int i = 1; i <= min(m,l); ++i)
            {
                dp[pos^1][i] = (xx[i]*a[l][i])%mod;
                tmp = (tmp+dp[pos^1][i])%mod;
            }
        }
        else
        {
            for(int i = 1; i <= min(m,l); ++i)
            {
                dp[pos^1][i] = (((((((xx[i]*sum)%mod)-((c[i]*dp[pos][i])%mod))%mod)+mod)%mod)*a[l][i])%mod;
                tmp = (tmp+dp[pos^1][i])%mod;
            }
        }

        sum = tmp;
        pos ^= 1;
    }

    printf("%lld\n",sum);

    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值