• codeforces 140E.New Year Garland


    传送门:

    解题思路:

    要求相邻两行小球颜色集合不同,并且限制行内小球相邻不同。

    由此可得:每行小球排列都是独立与外界的,

    所以答案应该是对于所有行的颜色集合分类,在将行内的答案乘到上面。

    先考虑如何分类:

    我们可以确定对于每行所取的颜色种类$x=|S|$,

    若相邻两行$i,j$,其$x_i!=x_j$,那么一定是合法的,有$C_m^x$种选择方法。

    而对于相邻两行$x_i=x_j$,对于行$i$的一种方案,只有一种可能使得$S_i=S_j$,

    所以可以使用容斥来计算答案

    综上所述,按照每行的颜色种类数来分类或许是可行的。

    所以说我们表示出答案:

    设该行共用$x$种颜色的方案数为$f(x)$,$f(x)$是对于所有的种类进行计数的,所以可以直接与颜色数为$x$的其他计数变量相乘,设第$n$行中颜色为$i$对整体的贡献为$ans_{n,i}$则:

    $ans_n=C_m^x*f(x)*ans_{n-1}-f(x)*ans_{n-1,j}$

    $ans_n=sumlimits_{c=1}^{l_n}ans_{n,c}$

    用函数$g_{i,j}$表示就是$ans_i=sum g_{i,j}$表示前$i$行中最后一行用$j$个颜色方案数。

    行间计数搞定了,就该考虑如何计算$f(x)$
    由于刚才设$f(x)$为该行的方案数。这个看起来不太好求。

    考虑什么决定了这个方案数。

    是该行的球数以及颜色数。

    所以不妨改写一下,$f(i,j)$表示用了$i$个球,共$j$种颜色的方案数,那么第$i$行的$f(x)$重写为$f(l_i,x)$

    考虑一个一个来添加球,由于要求和前一个颜色不同,即可获得递推式:

    $f(i,j)=f(i-1,j-1)+f(i-1,j)*(j-1)$

    由于每次用到没有用过的颜色顺序是有序的,而所求的是对球颜色顺序无要求,那么最后使用的时候应写成:
    $j!*f(i,j)$

    球颜色数不会多于总数,那么$f(i,j)$,可以用二维数组来储存


    最后一个问题就是p不是质数不能直接用逆元。

    难不成要用拓展Lucas?

    组合数可以提出来:

    那么答案就是:

    $g_{i,j}=A_{m}{j}*f(i,j)*sum{g_{i-1}}-j!*f(i,j)*g_{i-1,j}$

    其中$f_{(i,j)},j!,A_m^j$预处理就可以很愉快地A掉这道题目了

    代码:

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 typedef long long lnt;
     5 lnt n,m,p;
     6 lnt lmax;
     7 lnt Am[50001];
     8 lnt fac[50001];
     9 lnt l[2000000];
    10 lnt f[5010][5010];
    11 lnt g[2][5010];
    12 int main()
    13 {
    14     scanf("%lld%lld%lld",&n,&m,&p);
    15     fac[0]=Am[0]=f[0][0]=1;
    16     for(int i=1;i<=n;i++)
    17         scanf("%lld",&l[i]),lmax=std::max(lmax,l[i]);
    18     for(int i=1;i<=lmax;i++)Am[i]=Am[i-1]*(m-i+1)%p;
    19     for(int i=1;i<=lmax;i++)fac[i]=fac[i-1]*i%p;
    20     for(int i=1;i<=lmax;i++)for(int j=1;j<=i&&j<=m;j++)
    21         f[i][j]=(f[i-1][j-1]+f[i-1][j]*(j-1))%p;
    22     lnt ans=1,sum=0;
    23     for(int i=1;i<=n;i++)
    24     {
    25         for(int j=1;j<=l[i];j++)
    26         {
    27             lnt tmp=(Am[j]*ans-g[i&1^1][j]*fac[j])%p;
    28             g[i&1][j]=f[l[i]][j]*tmp%p;
    29             sum+=g[i&1][j];
    30         }
    31         ans=(sum%p+p)%p;
    32         sum=0;
    33         memset(g[i&1^1],0,sizeof(g[0]));
    34     }
    35     printf("%lld
    ",ans);
    36     return 0;
    37 }
  • 相关阅读:
    nodejs+mongoose操作mongodb副本集实例
    创建mongodb副本集操作实例
    SpringBoot(二)Web整合开发
    SpringBoot(一)走进Springboot的世界
    Git(二)Git几个区的关系与Git和GitHub的关联
    Git(一)之基本操作详解
    HttpClient(二)HttpClient使用Ip代理与处理连接超时
    HttpClient(一)HttpClient抓取网页基本信息
    Jsoup(一)Jsoup详解(官方)
    MongoDB(一)环境搭建与初始配置
  • 原文地址:https://www.cnblogs.com/blog-Dr-J/p/10356883.html
Copyright © 2020-2023  润新知