4254. 【五校联考7day2】集体照
Description&Data Constraint
一年一度的高考结束了,我校要拍集体照。本届毕业生共分 \(n\) 个班,每个班的人数为 \(A_i\)。这次拍集体照的要求非常奇怪:所有学生站一排,且相邻两个学生不能同班。现在,安排这次集体照的老师找到了你,想问问你一共有多少种方案。方案数可能很大,最终结果对 \(1,000,000,007\) 取模。
\(1\le n\le 50,1\le A_i\le 50, \sum A_i\le1500\)
Solution
设 \(f_{i,j}\) 表示前 \(i\) 个班有 \(j\) 个位置相邻两个是同班的。统计 \(A_i\) 的前缀和 \(sum_i\)。
由于同班的每个人交换位置是不同的方案,我们可以最后乘上 \(\prod A_i\)
有转移方程 \(f_{i,j+A_i-k-l}=f_{i-1,j}\times C_{A_i-1}^{k-1}\times C_j^l\times C_{sum_{i-1}+1-j}^{k-l}\)。枚举 \(k\) 和 \(l\)。其中 \(k\) 表示把 \(A_i\) 分成 \(k\) 组,\(l\) 表示这 \(l\) 组插入在 \(j\) 中的 \(l\) 个位置。
- \(C^{k-1}_{a_{i-1}}\) 表示把 \(A_i\) 分成 \(k\) 组的方案数(插板)
- \(C_j^l\) 表示 \(j\) 选 \(l\) 的方案数。
- \(C_{sum_{i-1}+1-j}^{k-l}\) 表示在其他位置插入剩下的 \(k-l\) 组的方案数。
最后答案就是 \(f_{n,0}\times\prod A_i!\)
预处理组合数和阶乘。
Code
#include<cstdio>
#include<algorithm>
#define mod 1000000007
#define ll long long
#define N 55
#define A 1505
using namespace std;
ll n,ans,a[N],jc[A],c[A][A],f[N][A],sum[N];
int main()
{
freopen("photo.in","r",stdin);
freopen("photo.out","w",stdout);
jc[0]=c[0][0]=1;
for (int i=1;i<=A;++i)
jc[i]=jc[i-1]*i%mod;
for (int i=1;i<=A;++i)
c[i][0]=c[i][i]=1;
for (int i=2;i<=A;++i)
for (int j=1;j<i;++j)
c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
scanf("%lld",&n);
for (int i=1;i<=n;++i)
{
scanf("%lld",&a[i]);
sum[i]=sum[i-1]+a[i];
}
f[1][sum[1]-1]=1;
for (int i=2;i<=n;++i)
for (int j=0;j<=sum[i-1];++j)
if (f[i-1][j])
for (int k=1;k<=a[i];++k)
for (int l=0;l<=min(k,j);++l)
f[i][j+a[i]-k-l]=(f[i][j+a[i]-k-l]+f[i-1][j]*c[a[i]-1][k-1]%mod*c[j][l]%mod*c[sum[i-1]+1-j][k-l]%mod)%mod;
ans=f[n][0];
for (int i=1;i<=n;++i)
ans=ans*jc[a[i]]%mod;
printf("%lld\n",ans);
return 0;
}