1:求相同班级的学生不相邻的全排列
f[i][j]代表已经处理完了前i个班级,有多少个空隙左边和右边的同学的班级相同。
我们考虑把第i个班级的同学分成k组,然后有u组分在了左边和右边相同的空隙中,其他的分在了左边和右边不相同的空隙中。
首先把把a[i]个学生分成k组,所以这个分法一共有C[a[i]-1][k-1];
然后把这k组同学分成u组,所以一共的分法是C[k][u];//这个东西是不能加的,想一下小球的问题就行了。
然后我把这u组放在j个空隙中,所有的可能性 C[j][u];
然后我把剩下的k-u组放在剩下的空隙中所有的可能性C[sum+1-j][k-u];
所以这个时候剩下的使左边和右边相同的空隙 j-u+b[i]-k;这个画个图就能很好的看出来了。
然后最后的结果就是f[n][0]然后在让所有的同学不同就行了,然后就是乘每个班级人数的全排列就行了。
#include<bits/stdc++.h>
using namespace std;
const int MOD=1e9+7;
long long C[699][600];
long long dp[50][500];
long long fac[600];
int a[600];
void inist()
{
memset(C,0,sizeof(C));
for(int i=0;i<=500;i++)
{
C[i][0]=1;
for(int j=1;j<=i;j++)
{
C[i][j]=(C[i-1][j]+C[i-1][j-1])%MOD;
}
}
fac[0]=1;
for(int i=1;i<=500;i++) fac[i]=fac[i-1]*i%MOD;
}
int main()
{
inist();
int t,n;
scanf("%d",&t);
int id=0;
while(t--)
{
id++;
memset(dp,0,sizeof(dp));
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
dp[1][a[1]-1]=1;
// sum=a[1];
int sum=0;
for(int i=2;i<=n;i++)
{
sum+=a[i-1];
for(int j=0;j<sum;j++)
{
if(!dp[i-1][j]) continue;
for(int k=1;k<=a[i];k++)
{
for(int u=0;u<=k&&u<=j;u++)
{
long long temp=dp[i-1][j]%MOD;
temp=temp*C[a[i]-1][k-1]%MOD;
//temp*=C[k][u]%MOD; 想一下小球放在盒子里
temp=temp*C[j][u]%MOD;
temp=temp*C[sum+1-j][k-u]%MOD;
dp[i][j-u+a[i]-k]=(dp[i][j-u+a[i]-k]+temp)%MOD;
}
}
}
}
long long ans=dp[n][0];
for(int i=1;i<=n;i++)
{
ans=ans*fac[a[i]]%MOD;
}
printf("Case %d: ",id);
printf("%lld\n",(ans%MOD+MOD)%MOD);
}
}