zoukankan      html  css  js  c++  java
  • CF Gym 102028G Shortest Paths on Random Forests

    CF Gym 102028G Shortest Paths on Random Forests


    抄题解×1

    蒯板子真jir舒服。

    构造生成函数,(F(n))表示(n)个点的森林数量(本题都用EGF)。怎么求呢

    (f(n)=n^{n-2})表示(n)个点的树数量,根据(exp)定义,(e^x=sum_{i=0}^{infty}frac{x^i}{i!})。那么(F=exp f),感性理解就是如果选(i)个联通块拼起来就除以(i!),很对的样子。

    那么期望就等于所有森林的答案之和除以(F(n))

    (G(n))表示(n)个点所有森林的【可达】【有序】点对之和。

    显然的转移:(G(n)=sum_{i=1}^nF(x-i)A(i))(A(i))就是分离出了一个(i)个点的联通块,这个联通块有(i^{i-2})种树,而且可达点对有(C_n^2)个。

    所以(A(n)=i^{i-2}cdot C_n^2)

    (H(n))表示(n)个点所有森林的【连通】点对【贡献】(路径长度平方)和。

    路径长度平方可以看做在路径上选1或2条边的方案数。选边看成切开这条边切成两个联通块。设(B(n))表示一个(n)个点的连通块的贡献值。(B(n)=n^{n-2}cdot ncdot n)(n^{n-2})是树的数量,还有两个(n)表示要把整棵树串起来选出的两个端点。

    (x^2=2C_x^2+C_x^1),要对应系数,就是选2条边的方案数×2+选1条边的,也就是3个连通块的×2+2个连通块的。(H(n)=2B(n)^3+B(n)^2)

    注意一条路径会被正反算两次,(H)要除个2

    然后可以求答案了。如果(m)不管答案就是(frac{H(n)}{2F(n)})(H)没除过2),但是有(m)所以要知道不可达的点对之和,这个值就是(C_n^2F(n)-G(n))。总答案为(frac{m^2(C_n^2F(n)-G(n))+0.5H(n)}{F(n)})

    #include<bits/stdc++.h>
    #define il inline
    #define vd void
    #define mod 998244353
    #define poly std::vector<int>
    typedef long long ll;
    il ll gi(){
        ll x=0,f=1;
        char ch=getchar();
        while(!isdigit(ch))f^=ch=='-',ch=getchar();
        while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
        return f?x:-x;
    }
    il int pow(int x,int y){
        int ret=1;
        while(y){
            if(y&1)ret=1ll*ret*x%mod;
            x=1ll*x*x%mod;y>>=1;
        }
        return ret;
    }
    #define maxn 524289
    poly pA,pB;
    int rev[maxn],_lstN,P[maxn],iP[maxn];
    il vd ntt(int*A,int N,int t){
        for(int i=0;i<N;++i)if(rev[i]>i)std::swap(A[i],A[rev[i]]);
        for(int o=1;o<N;o<<=1){
            int W=t?P[o]:iP[o];
            for(int*p=A;p!=A+N;p+=o<<1)
                for(int i=0,w=1;i<o;++i,w=1ll*w*W%mod){
                    int t=1ll*w*p[i+o]%mod;
                    p[i+o]=(p[i]-t+mod)%mod;p[i]=(p[i]+t)%mod;
                }
        }
        if(!t){
            int inv=pow(N,mod-2);
            for(int i=0;i<N;++i)A[i]=1ll*A[i]*inv%mod;
        }
    }
    int N,lg;
    il vd setN(int n){
        N=1,lg=0;
        while(N<n)N<<=1,++lg;
        if(N!=_lstN)for(int i=0;i<N;++i)rev[i]=(rev[i>>1]>>1)|((i&1)<<lg-1);
    }
    il vd ntt(poly&a,int t){
        static int A[maxn];
        for(int i=0;i<a.size();++i)A[i]=a[i];memset(A+a.size(),0,4*(N-a.size()));
        ntt(A,N,t);
        a.resize(N);
        for(int i=0;i<N;++i)a[i]=A[i];
        int s=a.size();while(s&&!a[s-1])--s;
        a.resize(s);
    }
    il poly mul(poly a,poly b,int newn){
        setN(a.size()+b.size()-1);
        ntt(a,1),ntt(b,1);
        for(int i=0;i<N;++i)a[i]=1ll*a[i]*b[i]%mod;
        ntt(a,0);a.resize(newn);
        return a;
    }
    il poly operator+(poly a,const poly&b){
        if(a.size()<b.size())a.resize(b.size());
        for(int i=0;i<a.size();++i)if(i<b.size())a[i]=(a[i]+b[i])%mod;
        return a;
    }
    il poly operator-(poly a,const poly&b){
        if(a.size()<b.size())a.resize(b.size());
        for(int i=0;i<a.size();++i)if(i<b.size())a[i]=(a[i]-b[i]+mod)%mod;
        return a;
    }
    il poly operator*(poly a,int b){
        for(auto&i:a)i=1ll*i*b%mod;
        return a;
    }
    il poly qiudao(poly a){
        for(int i=0;i<a.size()-1;++i)a[i]=1ll*a[i+1]*(i+1)%mod;
        a.erase(a.end()-1);
        return a;
    }
    il poly jifen(poly a){
        a.insert(a.begin(),0);
        for(int i=1;i<a.size();++i)a[i]=1ll*a[i]*pow(i,mod-2)%mod;
        return a;
    }
    il poly getinv(poly a){
        if(a.size()==1)return poly(1,pow(a[0],mod-2));
        int n=a.size(),m=a.size()+1>>1;
        poly _a(m);
        for(int i=0;i<m;++i)_a[i]=a[i];
        poly b=getinv(_a);
        setN(n+m*2-2);
        ntt(a,1);ntt(b,1);
        for(int i=0;i<N;++i)a[i]=1ll*a[i]*b[i]%mod*b[i]%mod;
        ntt(a,0),ntt(b,0);
        a.resize(n);
        return b*2-a;
    }
    il poly getln(poly a,int n=-1){
        if(n==-1)n=a.size();
        a.resize(n);
        return jifen(mul(qiudao(a),getinv(a),n));
    }
    il poly getexp(poly a){
        if(a.size()==1)return a[0]=1,a;
        int n=a.size(),m=a.size()+1>>1;
        poly _a(m);
        for(int i=0;i<m;++i)_a[i]=a[i];
        poly b=getexp(_a);
        return mul(b,poly(1,1)-getln(b,a.size())+a,a.size());
    }
    il vd poly_init(){
    	int G=3,iG=332748118;
        for(int i=1;i<maxn;i<<=1)P[i]=pow(G,(mod-1)/(i<<1)),iP[i]=pow(iG,(mod-1)/(i<<1));
    }
    int fact[200010],ifact[200010],qn[200010],qm[200010];
    int main(){
    #ifdef XZZSB
        freopen("in.in","r",stdin);
        freopen("out.out","w",stdout);
    #endif
        poly_init();
    	int N=0,T=gi();
    	for(int i=1;i<=T;++i)qn[i]=gi(),qm[i]=gi(),N=std::max(N,qn[i]+1);
    	poly F(N),G(N);
    	fact[0]=1;for(int i=1;i<N;++i)fact[i]=1ll*fact[i-1]*i%mod;
    	ifact[N-1]=pow(fact[N-1],mod-2);for(int i=N-2;~i;--i)ifact[i]=1ll*ifact[i+1]*(i+1)%mod;
    	F[1]=1;for(int i=2;i<N;++i)F[i]=1ll*ifact[i]*pow(i,i-2)%mod;
    	F=getexp(F);
    	for(int i=2;i<N;++i)G[i]=1ll*pow(i,i-2)*(1ll*i*(i-1)/2%mod)%mod*ifact[i]%mod;
    	G=mul(F,G,N);
    	poly B(N);
    	for(int i=1;i<N;++i)B[i]=1ll*pow(i,i)*ifact[i]%mod;
    	poly B2(mul(B,B,N)),B3(mul(B,B2,N)),H(mul(F,B2+B3*2,N));
    	for(int i=1;i<N;++i)F[i]=1ll*F[i]*fact[i]%mod,G[i]=1ll*G[i]*fact[i]%mod,H[i]=1ll*H[i]*fact[i]%mod;
    	int n,m;
    	for(int i=1;i<=T;++i){
    		n=qn[i],m=qm[i];
    		m=1ll*m*m%mod*(1ll*n*(n-1)/2%mod*F[n]%mod-G[n]+mod)%mod;
    		printf("%lld
    ",1ll*pow(F[n],mod-2)*(m+1ll*H[n]*(mod+1>>1)%mod)%mod);
    	}
    	return 0;
    }
    
  • 相关阅读:
    链接错误error LNK2005可能原因之一
    ACCESS一些特殊数据类型
    flex&bison学习笔记(2)
    经典小故事
    spoj 2939 Query on a tree V 动态树分治
    spoj 913 Query on a tree II 倍增
    CEOI 2004 sweets 容斥原理
    poj 1741 Tree 树的分治
    zjoi 2007 hide 捉迷藏 动态树分治
    spoj 2798 Query on a tree again! 树链剖分
  • 原文地址:https://www.cnblogs.com/xzz_233/p/11000554.html
Copyright © 2011-2022 走看看