1. 程式人生 > >[組合數學][計數DP]JZOJ 4254 集體照

[組合數學][計數DP]JZOJ 4254 集體照

content \n put using www event 整數 img tar

Description

一年一度的高考結束了,我校要拍集體照。本屆畢業生共分n個班,每個班的人數為Ai。這次拍集體照的要求非常奇怪:所有學生站一排,且相鄰兩個學生不能同班。現在,安排這次集體照的老師找到了你,想問問你一共有多少種方案。方案數可能很大,最終結果對1,000,000,007取模。

Input

輸入文件名為photo.in。
第一行為為一個整數n。
第二行為n個正整數,分別為每個班的人數。

Output

輸出文件photo.out,共1行,為總方案數。

Sample Input

輸入1:
2
1 2
輸入2:
2 
1 3
輸入3:
3 
1 2 3
 

Sample Output

輸出1:
2
輸出2:
0
輸出3:
120

Data Constraint

對於30%,Sigma(Ai) <=10
對於另外10%,n=2
對於另外20%,n=3
對於100%,1<=n<=50,1<=Ai<=50, Sigma(Ai)<=1500

分析

組合數學題日常博客跳轉

技術分享圖片
#include <iostream>
#include <cstdio>
using namespace std;
typedef long long ll;
const
int N=5e2+10; const int Sigma=1.5e3+10; const ll P=1e9+7; int n; ll a[N],s[N],fact[N],C[Sigma][Sigma],f[N][Sigma]; int main() { freopen("photo.in","r",stdin);freopen("photo.out","w",stdout); scanf("%d",&n); fact[0]=C[0][0]=1; for (int i=1;i<N;i++) fact[i]=fact[i-1]*i%P;
for (int i=1;i<Sigma;i++) for (int j=0;j<=i;j++) C[i][j]=(C[i-1][j]+(j?C[i-1][j-1]:0))%P; for (int i=1;i<=n;i++) scanf("%lld",&a[i]),s[i]=s[i-1]+a[i]; f[1][s[1]-1]=1; for (int i=2;i<=n;i++) for (int j=0;j<=s[i-1];j++) if (f[i-1][j]) for (int k=1;k<=a[i];k++) for (int t=0;t<=min(k,j);t++) (f[i][j+a[i]-k-t]+=f[i-1][j]*C[j][t]%P*C[a[i]-1][k-1]%P*C[s[i-1]+1-j][k-t]%P)%=P; for (int i=1;i<=n;i++) (f[n][0]*=fact[a[i]])%=P; printf("%lld\n",f[n][0]); }
View Code

[組合數學][計數DP]JZOJ 4254 集體照