码迷,mamicode.com
首页 > 其他好文 > 详细

Luogu P4002 [清华集训2017]生成树计数

时间:2020-02-04 22:00:30      阅读:95      评论:0      收藏:0      [点我收藏+]

标签:eve   生成树   相关   i+1   +=   wap   http   void   构造   

Link
下文中的点指的是题目给的\(n\)个连通块。
因为题目给的式子只与度数和点权相关,因此考虑Prufer序列。
枚举每个点在Prufer序列中的出现次数,那么此时的贡献就是Prufer序列的个数乘上该生成树的价值。
\(ans=(n-2)!\sum\limits_{\sum d_i=n-2}\prod\limits_{i=1}^n\frac{a_i^{d_i+1}}{d_i!}(d_i+1)^m\sum\limits_{i=1}^n(d_i+1)^m\)
\(ans=(n-2)!\sum\limits_{i=1}^na_i\prod\limits_{\sum d_i=n-2}\prod\limits_{i=1}^n\frac{a_i^{d_i}}{d_i!}(d_i+1)^m\sum\limits_{i=1}^n(d_i+1)^m\)
\(ans=(n-2)!\prod\limits_{i=1}^na_i\sum\limits_{\sum d_i=n-2}\sum\limits_{i=1}^n\frac{a_i^{d_i}}{d_i!}(d_i+1)^{2m}\prod\limits_{j\ne i}\frac{a_j^{d_j}}{d_j!}(d_j+1)^m\)
前面的\((n-2)!\prod\limits_{i=1}^na_i\)可以直接算,那么我们要求的就是\(\sum\limits_{\sum d_i=n-2}\sum\limits_{i=1}^n\frac{a_i^{d_i}}{d_i!}(d_i+1)^{2m}\prod\limits_{j\ne i}\frac{a_j^{d_j}}{d_j!}(d_j+1)^m\),考虑以\(d_i\)为下标构造生成函数。
\(P(x)=\sum\frac{(i+1)^m}{i!}x^i,Q(x)=\sum\frac{(i+1)^{2m}}{i!}x^i,F(x)=\sum\limits_{i=1}^nQ(a_ix)\prod\limits_{j\ne i}P(a_ix)\),那么我们有\(ans=(n-2)!\prod\limits_{i=1}^na_i[x^{n-2}]F(x)\)
我们知道\(F(x)=\prod\limits_{i=1}^nP(a_ix)\sum\limits_{i=1}^n\frac{Q(a_ix)}{P(a_ix)}=\exp(\sum\limits_{i=1}^n{\ln P(a_ix)})\sum\limits_{i=1}^n\frac{Q(a_ix)}{P(a_ix)}\)
那么我们可以先求出\(\frac{Q(x)}{P(x)},\ln P(x)\),将\(a_ix\)代入并求和等价于\(x^k\)的系数要乘上\(\sum\limits_{i=1}^na_i^k\)
现在我们要求的变成了\(\forall k\in[0,n-2],\sum\limits_{i=1}^na_i^k\)
考虑答案的生成函数\(F(x)=\sum\limits_{j=0}^{n-2}\sum\limits_{i=1}^na_i^jx^j=\sum\limits_{i=1}^n\frac1{1-a_ix}\)
我们知道有\(\ln'(\frac1{1-a_ix})=\frac{-a_i}{1-a_ix}=-a_i\sum\limits(a_ix)^j\)
因此我们可以考虑先求出\(G(x)=\sum\limits_{i=1}^n-a_i\sum\limits_{j=0}^{n-2}(a_ix)^j\),然后由\(F(x)=-xG(x)+n\)得出答案。
我们知道\(G(x)=\sum\limits_{i=1}\ln'(\frac1{1-a_ix})=(\sum\limits_{i=1}\ln(\frac1{1-a_ix}))'=\ln'(\prod\limits_{i=1} \frac1{1-a_ix})\),那么分治NTT就行了。
总的时间复杂度是\(O(n\log n+n\log^2n)\),实际上exp的一个\(\log\)和分治的两个\(log\)差不多。

#include<cstdio>
#include<cstring>
#include<algorithm>
const int N=65537,P=998244353;
int read(){int x;scanf("%d",&x);return x;}
int inc(int a,int b){return a+=b-P,a+(a>>31&P);}
int dec(int a,int b){return a-=b,a+(a>>31&P);}
int mul(int a,int b){return 1ll*a*b%P;}
int pow(int a,int k){int r=1;for(;k;k>>=1,a=mul(a,a))if(k&1)r=mul(a,r);return r;}
int len,fac[N],inv[N],ifac[N],rev[N],w[N];
int getlen(int n){return 1<<(32-__builtin_clz(n));}
void init(int n)
{
    int lim=1<<(len=32-__builtin_clz(n)),g=pow(3,(P-1)/lim);
    w[lim>>1]=1,fac[0]=ifac[0]=inv[0]=fac[1]=ifac[1]=inv[1]=1;
    for(int i=1;i<lim;++i) rev[i]=(rev[i>>1]>>1)|(i&1? lim>>1:0);
    for(int i=(lim>>1)+1;i<lim;++i) w[i]=mul(w[i-1],g);
    for(int i=(lim>>1)-1;i;--i) w[i]=w[i<<1];
    for(int i=2;i<=lim;++i) fac[i]=mul(fac[i-1],i),ifac[i]=mul(ifac[i-1],inv[i]=mul(inv[P%i],P-P/i));
}
void NTT(int*a,int lim,int f)
{
    if(!~f) std::reverse(a+1,a+lim);
    for(int i=0,x=len-__builtin_ctz(lim);i<lim;++i) if(i<rev[i]>>x) std::swap(a[i],a[rev[i]>>x]);
    for(int i=1;i<lim;i<<=1) for(int j=0,d=i<<1;j<lim;j+=d) for(int k=0,x;k<i;++k) x=mul(a[i+j+k],w[i+k]),a[i+j+k]=dec(a[j+k],x),a[j+k]=inc(a[j+k],x);
    if(!~f) for(int i=0,x=P-(P-1)/lim;i<lim;++i) a[i]=mul(a[i],x);
}
void Inv(int*a,int*b,int deg)
{
    if(deg==1) return b[0]=pow(a[0],P-2),void();
    static int t[N];int lim=getlen(deg*2-2);
    Inv(a,b,(deg+1)>>1),memcpy(t,a,deg<<2),memset(t+deg,0,(lim-deg)<<2);
    NTT(t,lim,1),NTT(b,lim,1);
    for(int i=0;i<lim;++i) b[i]=mul(dec(2,mul(b[i],t[i])),b[i]);
    NTT(b,lim,-1),memset(b+deg,0,(lim-deg)<<2);
}
void Der(int*a,int*b,int deg){for(int i=1;i<deg;++i)b[i-1]=mul(a[i],i);b[deg-1]=0;}
void Int(int*a,int*b,int deg){for(int i=1;i<deg;++i)b[i]=mul(a[i-1],inv[i]);b[0]=0;}
void Ln(int*a,int*b,int deg)
{
    static int t[N];int lim=getlen(deg*2-2);
    Inv(a,t,deg),Der(a,b,deg),NTT(t,lim,1),NTT( b,lim,1);
    for(int i=0;i<lim;++i) t[i]=mul(t[i],b[i]);
    NTT(t,lim,-1),Int(t,b,deg),memset(t,0,lim<<2),memset(b+deg,0,(lim-deg)<<2);
}
void Exp(int*a,int*b,int deg)
{
    if(deg==1) return b[0]=1,void();
    static int t[N];int lim=getlen(deg*2-2);
    Exp(a,b,(deg+1)>>1),Ln(b,t,deg);
    for(int i=0;i<deg;++i) t[i]=dec(a[i],t[i]);
    memset(t+deg,0,(lim-deg)<<2),++t[0],NTT(t,lim,1),NTT(b,lim,1);
    for(int i=0;i<lim;++i) b[i]=mul(b[i],t[i]);
    NTT(b,lim,-1),memset(b+deg,0,(lim-deg)<<2),memset(t+deg,0,(lim-deg)<<2);
}
int n,m,ans,a[N],f[16][N],t[5][N];
void solve(int l,int r,int d)
{
    if(l==r) return f[d][0]=1,f[d][1]=P-a[l],void();
    int mid=(l+r)>>1,lim=getlen(r-l+1);
    solve(l,mid,d),solve(mid+1,r,d+1);
    memset(f[d]+mid-l+2,0,(lim-mid+l-2)<<2),memset(f[d+1]+r-mid+1,0,(lim-r+mid-1)<<2),NTT(f[d],lim,1),NTT(f[d+1],lim,1);
    for(int i=0;i<lim;++i) f[d][i]=mul(f[d][i],f[d+1][i]);
    NTT(f[d],lim,-1);
}
int main()
{
    n=read(),m=read(),init(n*2);int lim=getlen(n*2);
    if(n==1) return !printf("%d",m==0);
    for(int i=1;i<=n;++i) a[i]=read();
    solve(1,n,0),Ln(f[0],t[0],n+1),t[0][0]=n;
    for(int i=1;i<=n;++i) t[0][i]=dec(0,mul(i,t[0][i]));
    for(int i=0;i<n;++i) t[1][i]=mul(pow(i+1,m),ifac[i]),t[2][i]=mul(pow(i+1,2*m),ifac[i]);
    Ln(t[1],t[3],n),Inv(t[1],t[4],n),NTT(t[2],lim,1),NTT(t[4],lim,1);
    for(int i=0;i<lim;++i) t[2][i]=mul(t[2][i],t[4][i]);
    NTT(t[2],lim,-1),memset(t[1],0,lim<<2),memset(t[4],0,lim<<2);
    for(int i=0;i<n;++i) t[2][i]=mul(t[2][i],t[0][i]),t[1][i]=mul(t[3][i],t[0][i]);
    Exp(t[1],t[4],n),memset(t[2]+n,0,(lim-n)<<2),NTT(t[2],lim,1),NTT(t[4],lim,1);
    for(int i=0;i<lim;++i) t[2][i]=mul(t[2][i],t[4][i]);
    NTT(t[2],lim,-1),ans=mul(t[2][n-2],fac[n-2]);
    for(int i=1;i<=n;++i) ans=mul(ans,a[i]);
    printf("%d",ans);
}

Luogu P4002 [清华集训2017]生成树计数

标签:eve   生成树   相关   i+1   +=   wap   http   void   构造   

原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/12261597.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!