【NOI2017模拟6.29】呵呵

来源:互联网 发布:威锋软件源地址 编辑:程序博客网 时间:2024/05/17 22:58

题目

这里写图片描述
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

dpi1dpi!中的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;}
原创粉丝点击