[WC2019] 数树

[WC2019] 数树 

Zhang_RQ题解(本篇仅概述)

 

前言

有进步,只做了半天。。。。

一道具有极强综合性的数数好题!

强大的多合一题目

精确地数学推导和耐心。

有套路又不失心意。

 

融合了:

算法:

prufer序列及其扩展

树形Dp

容斥或者二项式定理

EGF

多项式Exp

 

先要会:

[学习笔记]prufer序列

[学习笔记]多项式指数函数

[学习笔记]生成函数

luoguP4841 城市规划

省选模拟赛第四轮 B——O(n^4)->O(n^3)->O(n^2)

 

然后开始刚题。

就是:

求各种情况下的y^(n-|T1∩T2|)的和

 

 

OP=0

哈希。

OP=1

枚举交集,统计符合的T2树的个数

prufer序列森林版扩展

但是会算重,连回去会多

考虑重组贡献

每个实际的$y^k$会被$k$小的时候计算,$y^k=(y+1-1)^k=∑C(j,i)(y-1)^i$

发现,只要把贡献的$y$成$y-1$,就大功告成啦!

$\Pi szi$可以套路地组合意义成为每个连通块选择一个的方案数,然后就$f[i][0/1]$就O(n)辣

注意每次选择交的边还有贡献

这里,第二个$y^{-1}$运算的时候,要变成$(y^{-1}-1)$

OP=2

还是枚举交集的边

发现和交集大小密切相关,所以枚举交集大小$s$​

然后$gs$设出来,

要枚举边再考虑连通块,不妨枚举连通块考虑贡献的边集

连通块的分配$\frac{n!}{(n-s)!}$,连通块内的连边$a^{a-2}$,连通块之间连边$\Pi a_i *n^{m-2}$

一些东西要平方

把$gs$带回去,然后无关的都提到前面去

把枚举连通块,变成EGF

然后再变成多项式指数函数的形式,大功告成!

 

 

#include<bits/stdc++.h>
#define reg register int
#define il inline
#define fi first
#define se second
#define mk(a,b) make_pair(a,b)
#define numb (ch^'0')
using namespace std;
typedef long long ll;
template<class T>il void rd(T &x){
    char ch;x=0;bool fl=false;
    while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
    for(x=numb;isdigit(ch=getchar());x=x*10+numb);
    (fl==true)&&(x=-x);
}
template<class T>il void output(T x){if(x/10)output(x/10);putchar(x%10+'0');}
template<class T>il void ot(T x){if(x<0) putchar('-'),x=-x;output(x);putchar(' ');}
template<class T>il void prt(T a[],int st,int nd){for(reg i=st;i<=nd;++i) ot(a[i]);putchar('\n');}

namespace Miracle{
const int N=1e5+5;
const int mod=998244353;
const int G=3;
const int GI=332748118;
int n,Y;
int mul(int x,int y){return (ll)x*y%mod;}
int ad(int x,int y){return x+y>=mod?x+y-mod:x+y;}
il int sub(int x,int y){return ad(x,mod-y);}
int qm(int x,int y){
    int ret=1;
    while(y){
        if(y&1) ret=mul(ret,x);
        x=mul(x,x);
        y>>=1;
    }
    return ret;
}
namespace sol0{
map<pair<int,int>,int>mp;
int cnt;
void main(){
    int x,y;
    for(reg i=1;i<n;++i){
        rd(x);rd(y);if(x>y) swap(x,y);mp[mk(x,y)]=1;
    }
    for(reg i=1;i<n;++i){
        rd(x);rd(y);if(x>y) swap(x,y);cnt+=mp[mk(x,y)];
    }
    printf("%d\n",qm(Y,n-cnt));
}

}
namespace sol1{
ll f[N][2];
int z;
int cos,ivc;
struct node{
    int nxt,to;
}e[2*N];
int hd[N],cnt;
void add(int x,int y){
    e[++cnt].nxt=hd[x];
    e[cnt].to=y;
    hd[x]=cnt;
}
void dfs(int x,int fa){
    f[x][0]=f[x][1]=1;
    for(reg i=hd[x];i;i=e[i].nxt){
        int y=e[i].to;
        if(y==fa) continue;
        dfs(y,x);
        //jiao and no
        ll od0=f[x][0],od1=f[x][1];
        f[x][0]=ad(mul(od0,f[y][1]),mul(od0,mul(f[y][0],cos)));
        f[x][1]=ad(mul(od1,f[y][1]),ad(mul(od0,mul(f[y][1],cos)),mul(od1,mul(f[y][0],cos))));
    }
}
void main(){
    int x,y;
    for(reg i=1;i<n;++i){
        rd(x);rd(y);add(x,y);add(y,x);
    }
    if(Y==1){
        printf("%d\n",qm(n,n-2));
        return;
    }
    z=ad(qm(Y,mod-2),mod-1);
    cos=mul(z,qm(n,mod-2));
    ivc=qm(cos,mod-2);
//    cout<<" cos "<<cos<<" ivc "<<ivc<<endl;
    dfs(1,0);
    ll ans=0;
    ans=mul(mul(qm(n,n-2),qm(Y,n)),f[1][1]);
    cout<<ans;
}
}
namespace sol2{
struct Poly{
    vector<int>f;
    Poly(){f.clear();}
    il int &operator[](const int &x){return f[x];}
    il const int &operator[](const int &x) const {return f[x];}
    il void resize(const int &n){f.resize(n);}
    il int size() const {return f.size();}
    il void cpy(Poly &b){f.resize(b.size());for(reg i=0;i<(int)f.size();++i)f[i]=b[i];}
    il void rev(){reverse(f.begin(),f.end());}
    il void clear(){f.clear();}
    il void read(const int &n){f.resize(n);for(reg i=0;i<n;++i)rd(f[i]);}
    il void out() const {for(reg i=0;i<(int)f.size();++i)ot(f[i]);putchar('\n');}
}R;
il int init(const int &n){int m;for(m=1;m<n;m<<=1);return m;}
il void rev(Poly &f){
    int lp=f.size();
    if(R.size()!=f.size()) {
        R.resize(f.size());
        for(reg i=0;i<lp;++i){
            R[i]=(R[i>>1]>>1)|((i&1)?lp>>1:0);
        }
    }
    for(reg i=0;i<lp;++i){
        if(i<R[i]) swap(f[i],f[R[i]]);
    }
}
il void NTT(Poly &f,int c){
    int n=f.size();rev(f);
    for(reg p=2;p<=n;p<<=1){
        int gen=(c==1)?qm(G,(mod-1)/p):qm(GI,(mod-1)/p);
        for(reg l=0;l<n;l+=p){
            int buf=1;
            for(reg k=l;k<l+p/2;++k){
                int tmp=mul(f[k+p/2],buf);
                f[k+p/2]=sub(f[k],tmp);
                f[k]=ad(f[k],tmp);
                buf=mul(buf,gen);
            }
        }
    }
    if(c==-1){
        int iv=qm(n,mod-2);for(reg i=0;i<n;++i) f[i]=mul(f[i],iv);
    }
}
il Poly Inv(const Poly &f,int n){
    if(n==1){
        Poly g;g.resize(1);g[0]=qm(f[0],mod-2);return g;
    }
    Poly g=Inv(f,(n+1)>>1),t;
    int m=init(n*2);
    t.resize(m);
    for(reg i=0;i<n;++i)t[i]=f[i];
    g.resize(m);
    NTT(g,1);NTT(t,1);
    for(reg i=0;i<m;++i)g[i]=mul(sub(2,mul(g[i],t[i])),g[i]);
    NTT(g,-1);g.resize(n);
    return g;
}
il void operator *=(Poly &f,Poly g){
    int st=f.size()+g.size()-1;
    int len=init(f.size()+g.size()-1);f.resize(len);g.resize(len);
    NTT(f,1);NTT(g,1);for(reg i=0;i<len;++i) f[i]=mul(f[i],g[i]);
    NTT(f,-1);
    f.resize(st);
}
il void operator *=(Poly &f,const int &c){for(reg i=0;i<f.size();++i) f[i]=mul(f[i],c);}
il Poly operator *(Poly f,const Poly &g){f*=g;return f;}
il Poly operator *(Poly f,const int &c){for(reg i=0;i<f.size();++i) f[i]=mul(f[i],c);return f;}
il void operator +=(Poly &f,const Poly &g){for(reg i=0;i<f.size();++i) f[i]=ad(f[i],g[i]);}
il void operator +=(Poly &f,const int &c){f[0]=ad(f[0],c);}
il Poly operator +(Poly f,const Poly &g){for(reg i=0;i<f.size();++i) f[i]=ad(f[i],g[i]);return f;}
il Poly operator +(Poly f,const int &c){f[0]=ad(f[0],c);return f;}
il void operator -=(Poly &f,const Poly &g){for(reg i=0;i<f.size();++i) f[i]=sub(f[i],g[i]);}
il void operator -=(Poly &f,const int &c){f[0]=sub(f[0],c);}
il Poly operator -(Poly f,const Poly &g){for(reg i=0;i<f.size();++i) f[i]=sub(f[i],g[i]);return f;}
il Poly operator -(Poly f,const int &c){f[0]=sub(f[0],c);return f;}
il Poly operator ~(const Poly &f){return Inv(f,f.size());}
il Poly operator /(Poly f,Poly g){int len=f.size()-g.size()+1;f.rev();g.rev();g.resize(len);f=f*(~g);f.resize(len);f.rev();return f;}
il Poly operator %(Poly f,Poly g){Poly s=f/g;f=f-g*s;f.resize(g.size()-1);return f;}
il Poly Inter(Poly f){int st=f.size();f.resize(st+1);for(reg i=st;i>=1;--i){f[i]=mul(f[i-1],qm(i,mod-2));}f[0]=0;return f;}
il Poly Diff(Poly f){int st=f.size();for(reg i=0;i<st-1;++i) f[i]=mul(f[i+1],(i+1));f.resize(st-1);return f;}
il Poly Ln(const Poly &f){Poly g=Diff(f),h=(~f);g=g*h;g.resize(f.size()-1);return Inter(g);}
il Poly Exp(const Poly &f,int n){
    if(n==1){
        Poly g;g.resize(1);g[0]=1;
        return g;
    }
    Poly g=Exp(f,(n+1)>>1);
    g.resize(n);
    g=g*(((Ln(g)*(mod-1))+1)+f);
    g.resize(n);
    return g;
}
il Poly Exp(const Poly &f){
    return Exp(f,f.size());
}
int jie[N],inv[N];
void main(){
    if(Y==1){
        printf("%d\n",mul(qm(n,n-2),qm(n,n-2)));
        return;
    }
    int z=ad(qm(Y,mod-2),mod-1);
    Poly g;
    g.resize(n+1);
    
    int ivz=qm(z,mod-2);
    jie[0]=1;
    for(reg i=1;i<=n;++i) jie[i]=mul(jie[i-1],i);
    inv[n]=qm(jie[n],mod-2);
    for(reg i=n-1;i>=0;--i) inv[i]=mul(inv[i+1],i+1);
    
    for(reg j=1;j<=n;++j){
        g[j]=mul(mul(mul(mul(n,n),ivz),inv[j]),qm(j,j));
    }
    g=Exp(g);
    ll ans=mul(mul(mul(mul(qm(Y,n),jie[n]),qm(qm(n,mod-2),4)),qm(z,n)),g[n]);
    cout<<ans;
}

}
int main(){
    int op;
    rd(n);rd(Y);rd(op);
    if(op==0) sol0::main();
    else if(op==1) sol1::main();
    else sol2::main();
    return 0;
}

}
signed main(){
    Miracle::main();
    return 0;
}

/*
   Author: *Miracle*
   Date: 2019/4/12 15:09:42
*/

 

转载于:https://www.cnblogs.com/Miracevin/p/10698164.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值