Description&Data Constraint
一年一度的高考结束了,我校要拍集体照。本届毕业生共分 (n) 个班,每个班的人数为 (A_i)。这次拍集体照的要求非常奇怪:所有学生站一排,且相邻两个学生不能同班。现在,安排这次集体照的老师找到了你,想问问你一共有多少种方案。方案数可能很大,最终结果对 (1,000,000,007) 取模。
(1le nle 50,1le A_ile 50, sum A_ile1500)
Solution
设 (f_{i,j}) 表示前 (i) 个班有 (j) 个位置相邻两个是同班的。统计 (A_i) 的前缀和 (sum_i)。
由于同班的每个人交换位置是不同的方案,我们可以最后乘上 (prod A_i)
有转移方程 (f_{i,j+A_i-k-l}=f_{i-1,j} imes C_{A_i-1}^{k-1} imes C_j^l imes 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} imesprod 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
",ans);
return 0;
}