【NOI2017模拟6.29】呵呵

题目

这里写图片描述
1n2000

解法

考虑一个特定形态的树的贡献,设点i的度数为d[i],那么答案就是:

(wdiidi)

考虑prufer序,一个度数为d[i]的点出现的次数是d[i]-1,那么就可以得到一个很显然的DP,f[i][j]表示前i个点的度数为i+j:
fi,j=d=0jfi1,jd(dn2(jd))wd+1i(d+1)

这个DP是 O(n3)
即答案要求的是这样一个东西:
Ans=d1+d2++dn=n2(n2)!di!wdi+1i(di+1)Ans=wi(n2)!d1+d2++dn=n21di!wdii(di+1)

然后考虑将 (di+1) 拆开,对于每个排列 1p1<p2<pkn 考虑 ki=1dpi 的贡献:
d1+d2++dn=n21di!wdiidpi

dpi 1dpi! 中的 1dpi 消掉,得到:
wpi(d1+d2++dn=n2kwdiidi!)

这样有什么好处呢?可以看到后面的式子变成了这样的形式: xii!
考虑生成函数:
fi(x)=ewix=i=0wiii!xi

那么前面的式子可以变成:
wpi([xn2k]fi(x))

有:
wpi([xn2k]e(wi)x)

wpi((wi)n2k(n2k)!)

综上所述有:
Ans=wi(n2)!k=0n2(1p1<p2<<pknwpi)(wi)n2k(n2k)!

中间那个可以直接 O(n2) 的DP处理(其实FFT也是可以的)
所以总时间复杂度是 O(n2)

Code

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<set>
#include<bitset>
#include<map>

#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;
typedef double db;

int get(){
    char ch;
    while(ch=getchar(),(ch<'0'||ch>'9')&&ch!='-');
    if (ch=='-'){
        int s=0;
        while(ch=getchar(),ch>='0'&&ch<='9')s=s*10+ch-'0';
        return -s;
    }
    int s=ch-'0';
    while(ch=getchar(),ch>='0'&&ch<='9')s=s*10+ch-'0';
    return s;
}

const int N = 2010;
const int mo = 1e+9+7;

int n;
LL a[N],js[N],ny[N],mi[N];
LL f[N];

LL quickmi(LL x,LL tim){
    LL ans=1;
    for(;tim;tim/=2,x=x*x%mo)
    if (tim&1)ans=ans*x%mo;
    return ans;
}

int main(){
    freopen("hehe.in","r",stdin);
    freopen("hehe.out","w",stdout);
    n=get();
    fo(i,1,n)a[i]=get();
    js[0]=1;
    fo(i,1,n)js[i]=js[i-1]*i%mo;
    ny[n]=quickmi(js[n],mo-2);
    fd(i,n-1,0)ny[i]=ny[i+1]*(i+1)%mo;
    mi[0]=1;
    fo(i,1,n)mi[1]=(mi[1]+a[i])%mo;
    fo(i,2,n)mi[i]=mi[i-1]*mi[1]%mo;
    f[0]=1;
    fo(i,1,n)
        fd(j,n,0)
        f[j+1]=(f[j+1]+f[j]*a[i]%mo)%mo;
    LL ans=0;
    fo(k,0,n-2)
    ans=(ans+f[k]*mi[n-2-k]%mo*ny[n-2-k]%mo)%mo;
    ans=ans*js[n-2]%mo;
    fo(i,1,n)ans=ans*a[i]%mo;
    printf("%lld\n",ans);
    fclose(stdin);
    fclose(stdout);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值