组合数+动态规划
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); } }