题目描述
解题思路
乍一看不是很会。
先考虑不是环怎么做。
考虑分类地计数,即把方案归到某一类型里,再分别计算每一个类型的数量来求答案。
最终一种方案肯定有若干段相同颜色段,我们可以直接考虑每一种颜色的划分贡献,然后再算出他们组合起来的方案数。
具体地,我们计算出f(i,j)表示把i个相同的球分成j段的贡献和,一种方案贡献为每段大小乘积。然后,我们把同颜色的i个球缩成j个,然后问题就变成要计算有多少种不同的排列方案,使得相同颜色的球不相邻,设为F
设a[i]表示颜色i分成的段数,那么答案就是∑a[1..n]∏if(c[i],a[i])∗F(a[1..n])∑a[1..n]∏if(c[i],a[i])∗F(a[1..n]),其中F就是排列方案。
考虑F怎么求,我们可以做容斥。对于一种a[1..n],考虑枚举一些相邻的同颜色球再缩起来,设剩下每种球个数b[1..n],这些球可以乱放,那么此时容斥贡献是(∑b[i])!∗∏Cb[i]−1a[i]−1∗(−1)a[i]−b[i]∗1b[i]!(∑b[i])!∗∏Ca[i]−1b[i]−1∗(−1)a[i]−b[i]∗1b[i]!。这个容斥我们可以DP地计算,每次枚举a[i]和b[i]即可,我们还可以在中途把f(c[i],a[i])f(c[i],a[i])也算进去,这样是没有问题的。设F[i][j]表示做到颜色i,∑k=1..ib[k]=j∑k=1..ib[k]=j的贡献和,最后再乘j!j!。
这样不是环的做法就出来了,转移的时候优化一下。
现在考虑环怎么办。我们可以用最小表示法,固定颜色1在开头,且1不能结尾。算出所有方案再乘∑c[i]∑c[i]。固定开头我们只需要让最后的j!j!变成(j−1)!(j−1)!,而枚举b[1]的时候乘1(b[1]−1)!1(b[1]−1)!。固定结尾类似。不过这样有个问题,如果颜色1分成k段,dp完乘∑c∑c的时候,这种方案会被算k次,我们在开始的时候除即可。
然后就做完了。
实现的时候没有减掉开头和结尾都是1的方案,不知道为什么减掉错了,估计是已经容斥掉了吧…
代码
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<map>
#include<bitset>
//开 O2!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
using namespace std;
#define fo(i,j,k) for(i=j;i<=k;i++)
#define fd(i,j,k) for(i=j;i>=k;i--)
#define cmax(a,b) (a=(a>b)?a:b)
#define cmin(a,b) (a=(a<b)?a:b)
typedef long long ll;
typedef double db;
const int N=5000+5,mo=1e9+7;
int fac[N],rev[N],f[105][105],F[55][N],sum[N],c[N],A,B,i,j,k,ans,re[N],n,trs[N];
int ksm(int x,int y)
{
if (x==-1) return (y%2)?-1:1;
int ret=1;
while (y)
{
if (y&1) ret=1ll*ret*x%mo;
y>>=1;
x=1ll*x*x%mo;
}
return ret;
}
void predo(int n)
{
fac[0]=1;
fo(i,1,n) fac[i]=1ll*fac[i-1]*i%mo;
rev[n]=ksm(fac[n],mo-2);
fd(i,n,1) rev[i-1]=1ll*rev[i]*i%mo;
re[0]=1;
fo(i,1,n) re[i]=1ll*rev[i]*fac[i-1]%mo;
}
int C(int m,int n)
{
return 1ll*fac[m]*rev[n]%mo*rev[m-n]%mo;
}
int main()
{
freopen("number.in","r",stdin);
//freopen("number.out","w",stdout);
scanf("%d",&n);
fo(i,1,n)
{
scanf("%d",c+i);
sum[i]=sum[i-1]+c[i];
}
predo(5e3);
f[0][0]=1;
fo(i,1,100)
fo(j,1,i)
fo(k,1,i)
f[i][j]=(f[i][j]+1ll*f[i-k][j-1]*k)%mo;
F[0][0]=1;
fo(i,0,n)
{
fo(B,1,c[i+1])
{
trs[B]=0;
fo(A,B,c[i+1])
{
trs[B]=(trs[B]+1ll*f[c[i+1]][A]*C(A-1,B-1)%mo*ksm(-1,A-B)*rev[B])%mo;
}
}
fo(j,0,sum[i])
fo(B,1,c[i+1])
F[i+1][j+B]=(F[i+1][j+B]+1ll*F[i][j]*trs[B])%mo;
}
fo(j,0,sum[n])
ans=(ans+1ll*F[n][j]*fac[j-1])%mo;
printf("%d\n",1ll*ans*sum[n]%mo);
}