您的位置:首页 > 其它

【TC_SRM697 Hard】【JZOJ5180】ConnectedStates 题解

2017-06-30 20:26 211 查看

题目大意



n<=2000, wi<=1e9

题解

稍微有点妙啊。。。

与度数有关的无根树计数,考虑 prufer 序。

暴力可以直接 O(n3) dp 计算答案。

考虑优化。

假设第 i 个数在 prufer 序的出现次数是 ai,那么你求的是:∑a1+a2+...+an=n−2(n−2)!a1!a2!...an!∏i=1n(ai+1)wai+1i

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

∑a1+a2+...+an=n−2∏ni=1(ai+1)waiia1!a2!...an!

接着考虑拆开 ∏(ai+1),拆开后的每一项就相当于我选择一些 ai 乘起来。假设我选择的是 ap1,ap2,...,apk,则有:

∑a1+a2+...+an=n−2∑p1,p2,...,pkap1×ap2×...×apk×∏ni=1waiia1!a2!...an!

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

∑p1,p2,...,pkwp1×wp2×...×wpk∑a1+a2+...+an=n−2−k∏ni=1waiia1!a2!...an!

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

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

====[xn−2−k]∏i=1n(∑j≥0wjixjj!)[xn−2−k]∏i=1newix[xn−2−k] e∑wix[xn−2−k]∑j≥0(∑wi)jxjj!(∑wi)n−2−k(n−2−k)!

于是就。。做完了。

代码

#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
=mi(fac
,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
[k]*mi(sumw,n-2-k)%mo*ny[n-2-k])%=mo;

printf("%lld\n",ans*prow%mo*fac[n-2]%mo);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: