版权声明:欢迎借鉴,谢绝抄搬。 https://blog.csdn.net/Gx_Man_VIP/article/details/86708590
题目大意:
有
个班(从
开始编号),每个班的人数为
,现在他们要排集体照,有
个要求。
①所有学生站一排。
②相邻两个学生不能同班。
问满足的排列的方案总数,答案对
取模
分析:
考虑计数
,
令
表示前
个班,有
个空隙是两边同班的排列的方案总数。
此时我们不考虑同班内人的不同,我们把一个班内的当成同类人,最后的结果只需要乘上
即可,因为每个班在满足序列合法的情况下可以由不同的排列方式。
初值:
假如第
个班,我将其分为
组,用其中的
组人去插入这
个空隙,
这时我令
为前
个班的人数之和
则可以推出转移方程:
,加入了
个同班人以后,多了
个满足两边同班的空隙,将其分为
组,此时少了
个满足两边同班的空隙,用
组人去分别在
个满足两边同班的空隙中选
个隔开,此时少了
个满足两边同班的空隙
:
个空隙中任选
个插入的方案数
:
个数分成
组的方案数(插板法)
:在剩余位置中插入
组数,即
个空隙中分别(左右两边也行)插入
组数的方案数
最后的答案就是
代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
#include <cstring>
#include <algorithm>
#define M 1505
#define N 55
using namespace std;
typedef long long ll;
const int modn = 1e9 + 7;
ll dp[N][M], C[M][M], sum[N], jc[M], a[N], mul;
int n;
void Pre_Work()
{
jc[1] = 1;
for (int i = 2; i <= 1500; i ++) jc[i] = (ll)jc[i - 1] * i % modn;
C[0][0] = 1;
for (int i = 1; i <= 1500; i ++)
{
C[i][0] = 1;
for (int j = 1; j <= i; j++) C[i][j] = ((ll)C[i - 1][j - 1] + C[i - 1][j]) % modn;
}
}
int main(){
freopen("photo.in", "r", stdin);
freopen("photo.out", "w", stdout);
scanf("%d", &n);
Pre_Work();
mul = 1;
for(int i = 1; i <= n; i++)
scanf("%lld", &a[i]),
sum[i] = sum[i - 1] + a[i], mul = mul * jc[a[i]] % modn;
dp[1][sum[1] - 1] = 1;
for (int i = 1; i < n; i++)
for (int j = 0; j < sum[i]; j++)
if (dp[i][j])
for (int k = 1; k <= a[i + 1]; k++)
for (int l = 0; l <= min(k, j); l++)
dp[i + 1][j + a[i + 1] - k - l] =
((dp[i + 1][j + a[i + 1] - k - l] % modn + dp[i][j] * C[j][l] % modn * C[a[i + 1] - 1][k - 1] % modn * C[sum[i] - j + 1][k - l]) % modn + modn) % modn;
printf("%lld\n", ((dp[n][0] * mul) % modn + modn) % modn);
return 0;
}