CF1857G Counting Graphs

发布时间 2023-09-08 10:49:18作者: NBest

2023-08-08 23:00:07 solution

题意:

求有多少个有 \(n\) 个节点的无向图,使其满足以下条件:

  • 无重边自环。
  • 有且只有一个最小生成树,且为给定树。
  • 最大边权不大于 \(S\)

\(998244353\) 取模。

思路:

其实就是让我们在给定的树加边使得最小生成树不改变。

考虑到在整棵树上我们可以加大于最大边权的任意边使得满足条件,那显然我们断掉最大边后,在各个联通块内随意加大于连通块最大边的边也可以满足条件。

那么我们可以先按照边权将所有边排序,一条一条加入边将节点合并。现在我们先考虑对于两个大小分别为 \(siz_x,siz_y\) 的连通块,用目前最大边 \(w\) 合并,对答案的贡献。

因为 \(x,y\) 内部的贡献已经算过了,所以合并之后显然我们最多只能在 \(x,y\) 之间新增 \(siz_x\times siz_y-1\) 条边,按照上面的理论,每条选了的边可以选 \(S-w\) 个边权,我们令 \(siz_x\times siz_y-1=m\),那么贡献就为:

\[\binom{m}{0}(S-w)^0+\binom{m}{1}(S-w)^1+\dots+\binom{m}{m}(S-w)^{m} \]

这显然是一个二项式定理的形式,所以可以将其转化为 \((S-w+1)^m\)

最后将所有贡献相乘即可。

\(Code\)

struct edge{
    int u,v;ll w;
    inline bool operator <(const edge &o)const{return w<o.w;}
}F[200005];
int T,n,fa[200005];
ll fac[200005],ifac[200005],siz[200005],S;
inline int find(int x){return (x==fa[x]?x:(fa[x]=find(fa[x])));}
inline int merge(int x,int y){
    if((x=find(x))==(y=find(y)))return siz[x];
    if(siz[x]>siz[y])x^=y^=x^=y;
    siz[y]+=siz[x],siz[x]=0,fa[x]=y;
    return siz[y]; 
}
ll qpow(ll x,ll w){
    ll res=1;
    for(;w;w>>=1,x=x*x%mod)if(w&1)res=res*x%mod;
    return res;
}
int main(){
    for(T=read();T--;){
        n=read(),S=read();
        fa[n]=n,siz[n]=1;
        for(int i=1;i<n;i++){
            fa[i]=i,siz[i]=1;
            int u=read(),v=read(),w=read();
            F[i]={u,v,w};
        }
        sort(F+1,F+n);
        ll ans=1;
        for(int i=1;i<n;i++){
            int u=find(F[i].u),v=find(F[i].v);
            ll sume=siz[u]*siz[v]-1;//可以加入的边数
            ll sum=qpow(S-F[i].w+1,sume);//二项式定理
            ans=ans*sum%mod;
            merge(u,v);
        }
        printf("%lld\n",ans);
    }
}