zoukankan      html  css  js  c++  java
  • CF140E New Year Garland (计数问题)

    用$m$种颜色的彩球装点$n$层的圣诞树。圣诞树的第$i$层恰由$a_{i}$个彩球串成一行,且同一层内的相邻彩球颜色不同,同时相邻两层所使用彩球的颜色集合不 同。求有多少种装点方案,答案对$p$取模。

    好神的计数问题,zwz Orz

    题解思路来自黄学长hzwer的博客

    先只考虑在一行内的彩球的方案数

    定义$g[i][j]$表示一共有$i$个球串成一行,一共用了$j$种颜色的方案数

    因为所有颜色都是等价的,我们可以利用最小表示法来简化计数,比如让颜色编号为$x+1$的球第一次出现的位置,在颜色编号为$x$的球之前。实际的方案数是$g[i][j]cdot j!$

    这样递推关系就简单多了

    加入一个新颜色的球,$g[i][j]+=g[i-1][j-1]$

    加入一个旧颜色的球,颜色不能和第$i-1$个球相同,$g[i][j]+=(j-1)g[i-1][j]$

    $g[i][j]=g[i-1][j-1]+(j-1)g[i-1][j]$

    在考虑行行之间的影响

    定义$f[i][j]$表示前$i$行,其中第$i$行选了$j$种颜色的方案数

    如果没有相邻两行集合不同这种限制

    $f[i][j]=C_{m}^{j}cdot g[a_{i}][j]cdot sum f[i-1][k]$

    如果加上限制,

    $f[i][j]=C_{m}^{j}cdot g[a_{i}][j]cdot j!sum f[i-1][k]-g[a_{i}][j]cdot j!cdot f[i-1][j]$

    $=A_{m}^{j}cdot g[a_{i}][j]sum f[i-1][k]-g[a_{i}][j]cdot j!cdot f[i-1][j]$

    利用前缀和优化可以$O(1)$转移

    $f[i][j]$的状态数也仅仅是$O(sum a_{i})$,用滚动数组记录

    $A_{m}^{j}$和$j!$可以通过预处理得到

    总结:由于本题的模数是非质数,用组合数计数会让问题变得复杂,且时间复杂度很难保证。如果本题保证模数为质数,可能会有很多其他做法,比如组合数+容斥等等,但应该都没有官方题解的思路简洁。出题者似乎引导我们走向突破口——消去组合数,突破口在于通过化简,把组合数化成排列数和阶乘,排列数和阶乘即使在模数为非质数的情况下,也能在$O(n)$时间预处理

     1 #include <cmath>
     2 #include <cstdio>
     3 #include <cstring>
     4 #include <algorithm>
     5 #define N1 5010
     6 #define M1 1000010
     7 #define dd double
     8 #define ll long long 
     9 using namespace std;
    10 
    11 int gint()
    12 {
    13     int ret=0,fh=1;char c=getchar();
    14     while(c<'0'||c>'9'){if(c=='-')fh=-1;c=getchar();}
    15     while(c>='0'&&c<='9'){ret=ret*10+c-'0';c=getchar();}
    16     return ret*fh;
    17 }
    18 int n,m,mx,P;
    19 
    20 int f[2][N1],g[N1][N1],am[N1],mul[N1],a[M1];
    21 
    22 int main()
    23 {
    24     int i,j,x;
    25     scanf("%d%d%d",&n,&m,&x);
    26     for(i=1;i<=n;i++) scanf("%d",&a[i]), mx=max(mx,a[i]);
    27     const int p=x;
    28     g[0][0]=g[1][1]=1;
    29     for(i=2;i<=mx;i++) for(j=1;j<=i;j++) g[i][j]=(1ll*(j-1)*g[i-1][j]%p+g[i-1][j-1])%p;
    30     mul[0]=mul[1]=1; am[0]=1;
    31     for(i=1;i<=min(m,mx);i++) am[i]=1ll*am[i-1]*(m-i+1)%p;
    32     for(i=2;i<=mx;i++) mul[i]=1ll*mul[i-1]*i%p;
    33     int now=1,pst=0,snow=0,spst=1;
    34     f[pst][0]=1;
    35     for(i=1;i<=n;i++)
    36     {
    37         snow=0;
    38         for(j=1;j<=min(m,a[i]);j++)
    39             f[now][j]=(1ll*am[j]*g[a[i]][j]%p*spst%p-1ll*f[pst][j]*mul[j]%p*g[a[i]][j]%p+p)%p, (snow+=f[now][j])%=p;
    40         memset(f[pst],0,(min(m,a[i-1])+1)<<2); 
    41         swap(now,pst); swap(snow,spst);
    42     }
    43     printf("%d
    ",spst);
    44     return 0;
    45 }
  • 相关阅读:
    【C#进阶系列】06 类型和成员基础
    纪中5日T1 1564. 旅游
    纪中17日T1 2321. 方程
    纪中17日T2 2322. capacitor
    纪中10日T1 2313. 动态仙人掌
    纪中14日听课小结 图论 最短路 二分图 差分约束
    一个抓猫的游戏 消遣GAME 持续更新中!
    洛谷P1464 Function  HDU P1579 Function Run Fun
    洛谷P1976 鸡蛋饼
    纪中12日T1 2307. 选择
  • 原文地址:https://www.cnblogs.com/guapisolo/p/10356184.html
Copyright © 2011-2022 走看看