zoukankan      html  css  js  c++  java
  • [计数dp][数学] Jzoj P4254 集体照

    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

    题解

    • 题目大意:问有n个班,每个班有a[i]个人,问相邻两个人都不同班的方案数
    • n<=50,∑Ai<=1500,极其优秀的范围,考虑一下dp,设f[i][j]表示做到前i个班,有j个相邻为同班为位置的方案数
    • 首先,根据样例我们可以得出,每个班的每个人互换位置也算一种方案数,那么我们先考虑每个班里的人都是火柴人
    • 那么这个dp的状态转移方程为
    • 就是将A[i]分成k组,将t组插入分配到之前j个相邻的位置中,就是将t组分配插入到j个位置的方案数,就是将A[i]分成k组的方案数(就是挡板问题),就是将剩下的k-t组分配到剩下的原来合法的位置中的方案数
    • 然后怎么求呢,显然就可以预处理出组合数(杨辉三角)和阶乘(等下就知道了),答案就是F[n][0]
    • 那么我们返回来求每个班内不同人的分配,就是排列问题嘛,就是a[i]!
    • 所以最后的答案就为

    代码

     1 #include <cstdio>
     2 #include <iostream>
     3 #include <cstring>
     4 #define ll long long
     5 #define N 60
     6 #define M 1510
     7 #define mo 1000000007
     8 using namespace std;
     9 int n;
    10 ll a[N],sum[N],f[N][M],jc[M],c[M][M],ans;
    11 int main()
    12 {
    13     freopen("photo.in","r",stdin),freopen("photo.out","w",stdout),scanf("%d",&n);
    14     for (int i=1;i<=n;i++) scanf("%lld",&a[i]),sum[i]=sum[i-1]+a[i];
    15     jc[0]=c[0][0]=1; for (int i=1;i<=1500;i++) jc[i]=jc[i-1]*i%mo;
    16     for (int i=1;i<=1500;i++)
    17     {
    18         c[i][0]=c[i][i]=1;
    19         for (int j=1;j<=i-1;j++) c[i][j]=(c[i-1][j]+c[i-1][j-1])%mo;
    20     }
    21     f[1][sum[1]-1]=1;
    22     for (int i=2;i<=n;i++)
    23         for (int j=0;j<=sum[i-1];j++)
    24             if (f[i-1][j])
    25                 for (int k=1;k<=a[i];k++)
    26                     for (int p=0;p<=min(k,j);p++)
    27                         (f[i][j-p+a[i]-k]+=f[i-1][j]*c[j][p]%mo*c[a[i]-1][k-1]%mo*c[sum[i-1]+1-j][k-p]%mo)%=mo;
    28     ans=f[n][0];
    29     for (int i=1;i<=n;i++) ans=ans*jc[a[i]]%mo;
    30     printf("%lld",ans);
    31 }
  • 相关阅读:
    无限维
    黎曼流形
    why we need virtual key word
    TOJ 4119 Split Equally
    TOJ 4003 Next Permutation
    TOJ 4002 Palindrome Generator
    TOJ 2749 Absent Substrings
    TOJ 2641 Gene
    TOJ 2861 Octal Fractions
    TOJ 4394 Rebuild Road
  • 原文地址:https://www.cnblogs.com/Comfortable/p/10339519.html
Copyright © 2011-2022 走看看