zoukankan      html  css  js  c++  java
  • 【洛谷5296】[北京省选集训2019] 生成树计数(矩阵树定理)

    点此看题面

    • 给定一张(n)个点的无向完全图,每条边有一个边权。求所有生成树边权和的(k)次方之和。
    • (n,kle30)

    矩阵树定理

    考虑我们矩阵树定理维护的是所有生成树边权之积的和,因此一般遇到这种问题会去想把边权设成一个多项式。

    假设我们分别维护出了两块元素的前(k)次方和(记作(A_{0sim k})(B_{0sim k})),那么根据二项式定理展开则有:

    [F_i=sum_{j=0}^iC_i^jA_jB_{i-j} ]

    套路地暴拆组合数:

    [frac{F_i}{i!}=sum_{j=0}^ifrac{A_j}{j!} imesfrac{B_{i-j}}{(i-j)!} ]

    也就是说这相当于是两个指数型生成函数做卷积的形式。

    那么我们只要最初把权值为(w)的边的边权设成(sum_{i=0}^kfrac{w^i}{i!}x^i),然后套用矩阵树定理即可。

    最后答案就是解得多项式的第(k)项乘上(k!)(因为指数型生成函数中除去了(k!))。

    代码:(O(n^3k^2))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 30
    #define X 998244353
    using namespace std;
    int n,k,Fac[N+5],IFac[N+5];
    I int QP(RI x,RI y) {RI t=1;W(y) y&1&&(t=1LL*t*x%X),x=1LL*x*x%X,y>>=1;return t;}
    struct F
    {
    	int f[N+5];I F() {for(RI i=0;i<=k;++i) f[i]=0;}
    	I int& operator [] (CI x) {return f[x];}I int operator [] (CI x) Con {return f[x];}
    	I F operator + (Con F& g) Con {F t;for(RI i=0;i<=k;++i) t[i]=(f[i]+g[i])%X;return t;}//多项式加法
    	I F operator - (Con F& g) Con {F t;for(RI i=0;i<=k;++i) t[i]=(f[i]-g[i]+X)%X;return t;}//多项式减法
    	I F operator * (Con F& g) Con//多项式乘法
    	{
    		F t;for(RI i=0;i<=k;++i) for(RI j=0;j<=k-i;++j) t[i+j]=(t[i+j]+1LL*f[i]*g[j])%X;return t;
    	}
    	I F Inv() Con//多项式求逆
    	{
    		F t;for(RI i=0,j,x;i<=k;++i) {for(x=j=0;j^i;++j)
    			x=(x+1LL*t[j]*f[i-j])%X;t[i]=1LL*(!i-x+X)*QP(f[0],X-2)%X;}return t;
    	}
    	I friend bool operator ! (Con F& o) {for(RI i=0;i<=k;++i) if(o[i]) return 0;return 1;}//判是否全为0
    	I friend F operator - (Con F& o) {F t;for(RI i=0;i<=k;++i) t[i]=o[i]?X-o[i]:0;return t;}//所有系数取负
    }f,a[N+5][N+5];
    I F Gauss(CI n)//高斯消元求解行列式
    {
    	F t,s;s[0]=1;for(RI i=1,j,k;i<=n;s=s*a[i][i],++i)
    	{
    		if(!a[i][i]) {for(s=-s,j=i+1;j<=n&&!a[j][i];++j);for(k=i;k<=n;++k) swap(a[i][k],a[j][k]);}
    		for(j=i+1;j<=n;++j) for(t=-a[j][i]*a[i][i].Inv(),k=i;k<=n;++k) a[j][k]=a[j][k]+t*a[i][k];
    	}return s;
    }
    int main()
    {
    	RI i,j;for(scanf("%d%d",&n,&k),Fac[0]=i=1;i<=k;++i) Fac[i]=1LL*Fac[i-1]*i%X;
    	for(IFac[i=k]=QP(Fac[k],X-2);i;--i) IFac[i-1]=1LL*IFac[i]*i%X;
    	RI x,p,t;for(i=1;i<=n;++i) for(j=1;j<=n;++j)
    	{
    		for(scanf("%d",&x),p=0,t=1;p<=k;++p,t=1LL*t*x%X) f[p]=1LL*t*IFac[p]%X;//列出多项式
    		a[i][i]=a[i][i]+f,a[i][j]=a[i][j]-f;//矩阵树定理:度数矩阵-邻接矩阵
    	}
    	return printf("%d
    ",1LL*Fac[k]*Gauss(n-1)[k]%X),0;//最后答案要乘上k!
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    @字节跳动8年老Android面试官谈;Context都没弄明白凭什么拿高薪?
    @阿里面试官:Android面试这些原理都给我讲明白了,最低都是20k起步!
    @以后面试官再问你三次握手和四次挥手,直接把这一篇文章丢给他
    @备战2020年金三银四,看这一篇面试文章就够了(合适各级Java人员)
    字节跳动面试,第三面挂了,这原因我服了!
    太可惜了,四面字节跳动,我的offer竟被一道“算法题”给拦截了
    @java2019面试题北京
    @2019.07 Android 面试真题集锦
    2018 Java线程热门面试题,你知道多少?
    阿里大厂的148道核心面试题,(程序员必备学习方向)offer收割机 全会月薪50k不难
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu5296.html
Copyright © 2011-2022 走看看