JZOJ 4872.集体照

\(\text{Problem}\)

一年一度的高考结束了,我校要拍集体照。本届毕业生共分 \(n\) 个班,每个班的人数为 \(A_i\)。这次拍集体照的要求非常奇怪:所有学生站一排,且相邻两个学生不能同班。现在,安排这次集体照的老师找到了你,想问问你一共有多少种方案。方案数可能很大,最终结果对 \(10^9+7\) 取模。

\(\text{Solution}\)

考虑 \(dp\),按班级做,插入人
\(f_{i,j}\) 表示做到第 \(i\) 个班,已经出现 \(j\) 对相邻
那么转移就是当前班分 \(k\) 组,选 \(t\) 组插到 \(j\) 个空中,使他们分离

\(\text{Code}\)

#include<cstdio>
#include<algorithm>
#define LL long long 
#define re register
using namespace std;

const LL P = 1e9 + 7;
int n, a[55], sum[55];
LL fac[1505], C[1505][1505], f[55][1505];

inline void ADD(LL &x, LL y)
{
	x += y;
	if (x > P) x -= P;
}

int main() 
{
	freopen("photo.in", "r", stdin);
	freopen("photo.out", "w", stdout);
	scanf("%d", &n);
	for(re int i = 1; i <= n; i++) scanf("%d", &a[i]), sum[i] = sum[i - 1] + a[i];
	fac[0] = 1;
	for(re int i = 1; i <= sum[n]; i++) fac[i] = fac[i - 1] * i % P;
	for(re int i = 0; i <= sum[n]; i++) C[i][0] = 1;
	for(re int i = 1; i <= sum[n]; i++)
		for(re int j = 1; j <= i; j++) C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % P;
	f[1][a[1] - 1] = 1;
	for(re int i = 2; i <= n; i++)
		for(re int j = 0; j < sum[i - 1]; j++)
			for(re int k = 1; k <= a[i]; k++) 
				for(re int t = 0; t <= k; t++)
					ADD(f[i][j + a[i] - k - t], f[i - 1][j] * C[a[i] - 1][k - 1] % P * C[j][t] % P * C[sum[i - 1] + 1 - j][k - t] % P);
	LL ans = f[n][0];
	for(re int i = 1; i <= n; i++) ans = ans * fac[a[i]] % P;
	printf("%lld\n", ans);
}
posted @ 2021-08-11 11:30  leiyuanze  阅读(32)  评论(0编辑  收藏  举报