定义一个序列的权值为:把全部相邻的相同的数合并为一个集合后,全部集合的大小的乘积。spa
特别的,第一个数和最后一个数是相邻的。code
如今你有 \(n\) 种数,第 \(i\) 种有 \(c_i\) 个。求全部不一样的序列的权值的和。string
\(n\leq 50,c_i\leq 100\)it
考虑第一个数和最后一个数不相邻时怎么作。io
记 \(g_{i,j}\) 为出现了 \(i\) 次的数分红 \(j\) 个集合,全部集合大小的乘积的和。
\[ g_{i,j}=\sum_{k=1}^ig_{i-k,j-1}\times k \]
假设最后 \(i\) 分红了 \(a_i\) 个集合,那么答案就是 \(\prod_{i=1}^ng_{c_i,a_i}\) 再乘上方案数。class
方案数能够容斥求。im
具体来讲,把最后相邻且同色的球合并成一个大球。设最后有 \(b_i\) 个大球,那么容斥系数就是 \({(-1)}^{a_i-b_i}\),带容斥系数的方案数就是 \(\binom{a_i-1}{b_i-1}{(-1)}^{a_i-b_i}\)集合
最后这 \(\sum b_i\) 个球能够随意放,方案数是 \(\frac{(\sum b_i)!}{\prod b_i!}\)di
总的答案是
\[ \left(\prod_{i=1}^ng_{c_i,a_i}\binom{a_i-1}{b_i-1}{(-1)}^{a_i-b_i}\right)\frac{(\sum_{i=1}^nb_i)!}{\prod_{i=1}^n b_i!} \]
这样就能够 DP 了。(状态为 \(i\) 和 \(\sum b_i\))时间
考虑第一个数和最后一个数相邻时怎么作。
能够用最小表示法,令第一个数为 \(1\) 且 最后一个数不为 \(1\)(除非 \(n=1\))。
只须要在后面计算组合数的时候把 \(b_1-1\) 再除以 \(a_1\) 就能够获得第一个数为 \(1\) 的方案数。
把 \(b_1-2\) 再除以 \(a_1\) 就能够获得第一个数为 \(1\) 且最后一个数也是 \(1\) 的方案数。
除以 \(a_1\) 是由于一个方案会被算屡次。
再把方案数乘以 \(\sum c_i\) 就是答案了。
时间复杂度:\(O((\sum c_i)^2)\)
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; const ll p=1000000007; ll fac[5010],ifac[5010],inv[5010]; ll f[60][5010]; ll g[110][110]; int a[60]; int n; int s[60]; ll c[110][110]; ll c1[110],c2[110]; ll binom(int x,int y) { return x>=y&&y>=0?fac[x]*ifac[y]%p*ifac[x-y]%p:0; } int main() { #ifndef ONLINE_JUDGE freopen("c.in","r",stdin); freopen("c.out","w",stdout); #endif scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); s[i]=s[i-1]+a[i]; } inv[1]=fac[0]=fac[1]=ifac[0]=ifac[1]=1; for(int i=2;i<=5000;i++) { inv[i]=-p/i*inv[p%i]%p; fac[i]=fac[i-1]*i%p; ifac[i]=ifac[i-1]*inv[i]%p; } g[0][0]=1; for(int i=1;i<=100;i++) for(int j=1;j<=100;j++) for(int k=1;k<=i;k++) g[i][j]=(g[i][j]+g[i-k][j-1]*k)%p; f[0][0]=1; for(int i=1;i<n;i++) for(int j=1;j<=a[i];j++) for(int k=1;k<=j;k++) c[i][k]=(c[i][k]+g[a[i]][j]*binom(j-1,k-1)%p*((j-k)&1?-1:1))%p; for(int i=n;i<=n;i++) for(int j=1;j<=a[i];j++) for(int k=1;k<=j;k++) c1[k]=(c1[k]+g[a[i]][j]*binom(j-1,k-1)%p*((j-k)&1?-1:1)*inv[j])%p; for(int i=1;i<=n;i++) for(int j=1;j<=a[i];j++) for(int k=0;k<=s[i-1];k++) f[i][k+j]=(f[i][k+j]+f[i-1][k]*c[i][j]%p*binom(k+j,k))%p; ll ans=0; for(int j=1;j<=a[n];j++) for(int k=0;k<=s[n-1];k++) ans=(ans+f[n-1][k]*c1[j]%p*(binom(k+j-1,k)-binom(k+j-2,k)))%p; ans=ans*s[n]%p; ans=(ans+p)%p; printf("%lld\n",ans); return 0; }