【Topcoder SRM697 Hard】【JZOJ5180】ConnectedStates 题解

题目大意

  n2000, wi109n \le 2000,\ w_i \le 10^9

题解

  稍微有点妙啊。。。

  与度数有关的无根树计数,考虑 prufer 序。
  暴力可以直接 O(n3)O(n^3) dp 计算答案。

  考虑优化。

  假设第 ii 个数在 prufer 序的出现次数是 aia_i,那么求的是:

a1+a2+...+an=n2(n2)!a1!a2!...an!i=1n(ai+1)wiai+1\sum_{a_1+a_2+...+a_n=n-2} \frac{(n-2)!}{a_1!a_2!...a_n!} \prod_{i=1}^n (a_i+1)w_i^{a_i+1}

  其中与 aa 无关的是 (n2)!×wi(n-2)!×\prod w_i,去掉之后原式变成:

a1+a2+...+an=n2i=1n(ai+1)wiaia1!a2!...an!\sum_{a_1+a_2+...+a_n=n-2} \frac{\prod_{i=1}^n (a_i+1)w_i^{a_i}}{a_1!a_2!...a_n!}

  接着考虑拆开 (ai+1)\prod (a_i+1),拆开后的每一项就相当于我选择一些 aia_i 乘起来。假设我选择的是 ap1,ap2,...,apka_{p_1},a_{p_2},...,a_{p_k},则有:

a1+a2+...+an=n2p1,p2,...,pkap1×ap2×...×apk×i=1nwiaia1!a2!...an!\sum_{a_1+a_2+...+a_n=n-2} \sum_{p_1,p_2,...,p_k} \frac{a_{p_1}×a_{p_2}×...×a_{p_k}×\prod_{i=1}^n w_i^{a_i}}{a_1!a_2!...a_n!}

  上面的 aa 会跟下面的阶乘约掉,那么我可以一开始就给这一部分 aa11,然后乘上后面少了的 ww,相当于:

p1,p2,...,pkwp1×wp2×...×wpka1+a2+...+an=n2ki=1nwiaia1!a2!...an!\sum_{p_1,p_2,...,p_k} w_{p_1}×w_{p_2}×...×w_{p_k}\sum_{a_1+a_2+...+a_n=n-2-k} \frac{\prod_{i=1}^n w_i^{a_i}}{a_1!a_2!...a_n!}

  注意到我只要枚举 kk 的话,前后两部分就独立了。前面是个背包,所以现在化简后面。

  后面这个东西跟 EGF(指数型生成函数) 很像,相当于求 :

[xn2k]i=1n(j0wijxjj!)=[xn2k]i=1newix=[xn2k] ewix=[xn2k]j0(wi)jxjj!=(wi)n2k(n2k)!\begin{array}{rcl} &&[x^{n-2-k}]\prod_{i=1}^n (\sum_{j\ge0} \frac{w_i^jx^j}{j!})\\ &=&[x^{n-2-k}]\prod_{i=1}^n e^{w_ix}\\ &=&[x^{n-2-k}]~e^{\sum w_ix}\\ &=&[x^{n-2-k}]\sum_{j\ge0} \frac{(\sum w_i)^j x^j}{j!}\\ &=&\frac{(\sum w_i)^{n-2-k}}{(n-2-k)!} \end{array}

  于是就。。做完了。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include<cstdio>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;

typedef long long LL;

const int maxn=2005;
const LL mo=1e9+7;

int n,w[maxn];
LL sumw,prow=1;

LL fac[maxn],ny[maxn],f[maxn][maxn];
LL mi(LL x,LL y)
{
LL re=1;
for(; y; y>>=1, x=x*x%mo) if (y&1) re=re*x%mo;
return re;
}
void Pre()
{
fac[0]=ny[0]=1;
fo(i,1,n) fac[i]=fac[i-1]*i%mo;
ny[n]=mi(fac[n],mo-2);
fd(i,n-1,1) ny[i]=ny[i+1]*(i+1)%mo;

f[0][0]=1;
fo(i,1,n)
fo(j,0,i)
{
f[i][j]=f[i-1][j];
if (j) (f[i][j]+=f[i-1][j-1]*w[i])%=mo;
}
}

int main()
{
scanf("%d",&n);
fo(i,1,n)
{
scanf("%d",&w[i]);
if (!w[i]) {printf("0\n"); return 0;}
(sumw+=w[i])%=mo;
(prow*=w[i])%=mo;
}

Pre();

LL ans=0;
fo(k,0,n-2) (ans+=f[n][k]*mi(sumw,n-2-k)%mo*ny[n-2-k])%=mo;

printf("%lld\n",ans*prow%mo*fac[n-2]%mo);
}