传送门:
解题思路:
要求相邻两行小球颜色集合不同,并且限制行内小球相邻不同。
由此可得:每行小球排列都是独立与外界的,
所以答案应该是对于所有行的颜色集合分类,在将行内的答案乘到上面。
先考虑如何分类:
我们可以确定对于每行所取的颜色种类$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=\sum\limits_{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 #include2 #include 3 #include 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\n",ans); 36 return 0; 37 }