【TC_SRM697 Hard】【JZOJ5180】ConnectedStates 题解

来源:互联网 发布:js缺少对象 编辑:程序博客网 时间:2024/05/22 12:23

题目大意

这里写图片描述

      n<=2000 wi<=1e9



题解

      稍微有点妙啊。。。

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

      考虑优化。

      假设第 i 个数在 prufer 序的出现次数是 ai,那么你求的是:

a1+a2+...+an=n2(n2)!a1!a2!...an!i=1n(ai+1)wai+1i

      其中与 a 无关的是 (n2)!×wi,去掉之后原式变成:

a1+a2+...+an=n2ni=1(ai+1)waiia1!a2!...an!

      接着考虑拆开 (ai+1),拆开后的每一项就相当于我选择一些 ai 乘起来。假设我选择的是 ap1,ap2,...,apk,则有:
a1+a2+...+an=n2p1,p2,...,pkap1×ap2×...×apk×ni=1waiia1!a2!...an!

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

p1,p2,...,pkwp1×wp2×...×wpka1+a2+...+an=n2kni=1waiia1!a2!...an!

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

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

====[xn2k]i=1n(j0wjixjj!)[xn2k]i=1newix[xn2k] ewix[xn2k]j0(wi)jxjj!(wi)n2k(n2k)!

      于是就。。做完了。

代码

#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);}