zoukankan      html  css  js  c++  java
  • [清华集训2017] 生成树计数

    题目链接

    分析

    一类树(连出的边数集合一定)的贡献

    [mathbb{Ans}({d_n}|sum_id_i=2(n-1))=prod_ia_i^{d_i}prod_id_i^msum_{i}d_i^m ]

    引入Prufer序列,设(d_i)为点(联通块)在序列中出现的次数,转换

    [egin{aligned} mathbb{Ans}({d_n}|sum_{i}d_i=n-2) &=prod_ia_i^{d_i+1}prod_i{(d_i+1)^m}sum_{i}(d_i+1)^m\ &=prod_ia_i^{d_i+1}(d_i+1)^msum_{i}(d_i+1)^m\ end{aligned} ]

    那么枚举所有的Prufer的组合,总答案

    [egin{aligned} mathbb{Ans} &=sum_{sum_id_i=n-2}frac{(n-2)!}{prod_id_i!}prod_ia_i^{d_i+1}(d_i+1)^msum_{i}(d_i+1)^m\ &=(n-2)!prod_ia_isum_{sum_id_i=n-2}prod_ifrac{a_i^{d_i}(d_i+1)^m}{d_i!}sum_i(d_i+1)^{m}\ &=(n-2)!prod_ia_i(sum_{sum_id_i=n-2}sum_ifrac{a_i^{d_i}(d_i+1)^{2m}}{d_i!}prod_{i ot=j}frac{a_j^{d_i}(d_j+1)^m}{a_j!}) end{aligned} ]

    G8麻烦……请出生成函数

    [A(x)=sum_ifrac{x^i(i+1)^{2m}}{i!}\ B(x)=sum_ifrac{x^i(i+1)^m}{i!}\ F(x)=sum_iA(a_ix)prod_{i ot=j}B(a_jx) ]

    注意到([n-2]F(x))正是(mathbb{Ans})中非常数部分(括号注明部分)。于是需要处理(F(x))

    [F(x)=sum_ifrac{A(a_ix)}{B(a_ix)}prod_{i}B(a_ix)=sum_ifrac{A(a_ix)}{B(a_ix)}exp(sum_{i}ln(B(a_ix))) ]

    (lnexp)中视(a_ix)整体为变量,处理(frac{A(x)}{B(x)})(ln(B(x)))的系数,再用(a_ix)代换(x),求出(sumfrac{A(a_ix)}{B(a_ix)})以及(sumln(B(a_ix)))

    这两个过程本质相同:和函数(sum)的第(i)向系数是单个函数的(i)项系数(常量)乘上(sum_ka_k^i)

    于是涉及到一个序列的幂和,它的生成函数

    [f(x)=sum_isum_ja_j^ix^i=sum_isum_j(a_ix)^j=sum_ifrac{1}{1-a_ix}\ g(x)=sum_iln(frac{1}{1-a_ix})=sum_ifrac{-a_i}{1-a_ix}=-sum_isum_ja_i^{j+1}x^j\ f(x)=n-x imes g(x)\ g(x)=sum_iln((1-a_ix)^{-1})=-ln(prod_i1-a_ix) ]

    关于这个(ln)(x)为变量而非(a_ix),于是分治FFT处理(g(x)),设(L)为不小于(n)的2的幂,可以粗略的估计为复杂度

    [sum_d^{log L}frac{L}ddlog d=Lsum_d^{log L}log d=Llog(log(L)!) ]

    打表发现(log(log(L)!))(log(L))略大,远小于(log^2)所在规模,于是近似认为复杂度为(O(nlog n))

    最后慢慢推回去……

    实现

    有很多波折……目前洛谷rank1

    #include <bits/stdc++.h>
    #define ll long long
    using namespace std;
    
    const int N=200000;
    const int MOD=998244353;
    
    inline int qpow(int x,int y) {
        int c=1;
        for(; y; y>>=1,x=(ll)x*x%MOD) if(y&1) c=(ll)c*x%MOD;
        return c;
    }
    
    //------- POLYNOMIAL BEGIN -------
    int w[N],rev[N],_inv[N],lmt;
    inline void preDone(int len) {
        int l=0; lmt=1; _inv[1]=1;
        while(lmt<=len) lmt<<=1,l++;
        for(int i=0; i<lmt; ++i) {
            rev[i]=(rev[i>>1]>>1)|((i&1)<<(l-1));
            if(i>1) _inv[i]=(ll)_inv[MOD%i]*(MOD-MOD/i)%MOD;
        }
        int wlmt=qpow(3,(MOD-1)>>l),tmp=lmt>>1; w[tmp]=1;
        for(int i=tmp+1; i<lmt; ++i) w[i]=(ll)w[i-1]*wlmt%MOD;
        for(int i=tmp-1; i>0; --i) w[i]=w[i<<1];
        lmt=l;
    }
    inline void DFT(int a[],int len) {
        static unsigned long long tmp[N];
        int u=lmt-__builtin_ctz(len),T;
        for(int i=0; i<len; ++i) tmp[rev[i]>>u]=a[i];
        for(int m=1; m<len; m<<=1)
        for(int i=0,s=m<<1; i<len; i+=s)
        for(int j=0; j<m; ++j)
            T=tmp[i+j+m]*w[m+j]%MOD,tmp[i+j+m]=tmp[i+j]+MOD-T,tmp[i+j]+=T;
        for(int i=0; i<len; ++i) a[i]=tmp[i]%MOD;
    }
    inline void IDFT(int a[],int len) {
        reverse(a+1,a+len); DFT(a,len);
        ll T=MOD-(MOD-1)/len; 
        for(int i=0; i<len; ++i) a[i]=T*a[i]%MOD;
    }
    inline int getLen(int len) {
        return 1<<(32-__builtin_clz(len));
    }
    inline void getDer(int a[],int b[],int n) {
        for(int i=0; i<n-1; ++i) b[i]=(ll)(i+1)*a[i+1]%MOD; b[n-1]=0;
    }
    inline void getInt(int a[],int b[],int n) {
        for(int i=n-1; i>0; --i) b[i]=(ll)_inv[i]*a[i-1]%MOD; b[0]=0;
    }
    inline void getInv(int a[],int b[],int n) {
        static int tmp[N];
        if(n==1) {b[0]=qpow(a[0],MOD-2); return;}
        getInv(a,b,(n+1)>>1);
        int len=getLen(n<<1);
        for(int i=0; i<n; ++i) tmp[i]=a[i];
        for(int i=n; i<len; ++i) tmp[i]=0;
        DFT(tmp,len); DFT(b,len);
        for(int i=0; i<len; ++i) b[i]=(ll)b[i]*(2+MOD-(ll)b[i]*tmp[i]%MOD)%MOD;
        IDFT(b,len);
        for(int i=n; i<len; ++i) b[i]=0;
    }
    inline void getLn(int a[],int b[],int n) {
        static int tmp[N];
        getInv(a,tmp,n);
        getDer(a,b,n);
        int len=getLen(n<<1);
        DFT(tmp,len); DFT(b,len);
        for(int i=0; i<len; ++i) tmp[i]=(ll)b[i]*tmp[i]%MOD;
        IDFT(tmp,len);
        getInt(tmp,b,n);
        for(int i=n; i<len; ++i) b[i]=0;
        for(int i=0; i<len; ++i) tmp[i]=0;
    }
    inline void getExp(int a[],int b[],int n) {
        static int tmp[N];
        if(n==1) {b[0]=1; return;}
        getExp(a,b,(n+1)>>1);
        getLn(b,tmp,n);
        int len=getLen(n<<1);
        for(int i=0; i<n; ++i) tmp[i]=((i==0)+MOD-tmp[i]+a[i])%MOD;
        for(int i=n; i<len; ++i) tmp[i]=0;
        DFT(tmp,len); DFT(b,len);
        for(int i=0; i<len; ++i) b[i]=(ll)tmp[i]*b[i]%MOD;
        IDFT(b,len);
        for(int i=n; i<len; ++i) b[i]=0;
        for(int i=0; i<len; ++i) tmp[i]=0;
    }
    //------- POLYNOMIAL END -------
    
    int n,m,con,a[N],d[20][N];
    void solve(int l,int r,int p) {
        if(l==r) {
            d[p][0]=1;
            d[p][1]=MOD-a[l];
            return;
        }
        int mid=(l+r)>>1; 
        solve(l,mid,p);
        solve(mid+1,r,p+1);
        int len=getLen(r-l+1);
        for(int i=mid-l+2; i<len; ++i) d[p][i]=0;
        for(int i=r-mid+1; i<len; ++i) d[p+1][i]=0;
        DFT(d[p],len); DFT(d[p+1],len);
        for(int i=0; i<len; ++i) d[p][i]=(ll)d[p][i]*d[p+1][i]%MOD;
        IDFT(d[p],len);
    }
    int fc[N],fv[N];
    int S[N],A[N],B[N],P[N],Q[N];
    
    int main() {
     	//freopen("filename.in","r",stdin);
        fc[0]=fc[1]=fv[0]=fv[1]=1;
        for(int i=2; i<N; ++i) fv[i]=(ll)fv[MOD%i]*(MOD-MOD/i)%MOD;
        for(int i=2; i<N; ++i) fv[i]=(ll)fv[i-1]*fv[i]%MOD;
        for(int i=2; i<N; ++i) fc[i]=(ll)fc[i-1]*i%MOD;
        
        scanf("%d%d",&n,&m); 
        preDone((n+1)*4); con=fc[n-2];
        for(int i=1; i<=n; ++i) scanf("%d",a+i),con=(ll)con*a[i]%MOD;
        solve(1,n,0); 
        getLn(d[0],S,getLen(n)); //改成二的幂次似乎能缓解数组清空问题……
    	S[0]=n;
        for(int i=1; i<=n; ++i) S[i]=(MOD-(ll)S[i]*i%MOD)%MOD;
    
        for(int i=0; i<=n-2; ++i) {
            A[i]=(ll)fv[i]*qpow(i+1,2*m)%MOD;
            B[i]=(ll)fv[i]*qpow(i+1,m)%MOD;
        }
        getInv(B,P,n-1);
        getLn(B,Q,n-1);
        int len=getLen(n<<1);
        DFT(A,len); DFT(P,len);
        for(int i=0; i<len; ++i) P[i]=(ll)A[i]*P[i]%MOD;
        IDFT(P,len);
        for(int i=n-1; i<len; ++i) P[i]=0;
        for(int i=0; i<=n-2; ++i) {
            P[i]=(ll)P[i]*S[i]%MOD;
            Q[i]=(ll)Q[i]*S[i]%MOD;
        }
        memset(B,0,sizeof B); //这儿也是……
        getExp(Q,B,n-1);
        DFT(P,len); DFT(B,len);
        for(int i=0; i<len; ++i) P[i]=(ll)P[i]*B[i]%MOD;
        IDFT(P,len);
        
        printf("%lld
    ",(ll)con*P[n-2]%MOD);
        return 0;
    }
    
    
  • 相关阅读:
    第二章 搭建Android开发环境
    彻底修改 Windows 系统用户名
    第一章 Android系统移植与驱动开发概述
    返回一个整数数组中最大子数组的和。
    返回一个二维整数数组中最大子数组的和。
    返回一个整数数组中最大的子数组的和。
    四则运算
    关于南方Cass的使用感受
    数据结构-王道2017-第3章 栈和队列-栈和队列的应用
    数据结构-王道2017-第3章 栈和队列-队列
  • 原文地址:https://www.cnblogs.com/nosta/p/11079216.html
Copyright © 2011-2022 走看看