P5296 [北京省选集训2019]生成树计数

P5296 [北京省选集训2019]生成树计数

题意

求一个带权无向图所有生成树边权和的 k 次方的和。

思路

首先有一个结论:ai 的 EGF 卷 bi 的 EGF 等于 (a+b)i 的 EGF。即:

F(a)=i=0aixii!F(a+b)=F(a)F(b)

证明如下:

(a+b)k=i=0k(ki)aibki=i=0kk!i!(ki)!aibkii=0kaii!bki(ki)!k!=(a+b)ki=0kaii!bki(ki)!=(a+b)kk!

然后又有一个结论:度数矩阵减去邻接矩阵的余子式的行列式的值是图所有生成树边权积的和。其中,度数矩阵表示与其相连的边权的和,邻接矩阵为边权。这是矩阵树定理。

于是,我们将边权化为生成函数,然后利用矩阵树定理算出来答案的生成函数即可。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cctype>
#include<cstring>
#include<cmath>
using namespace std;
inline int read(){
	int w=0,x=0;char c=getchar();
	while(!isdigit(c))w|=c=='-',c=getchar();
	while(isdigit(c))x=x*10+(c^48),c=getchar();
	return w?-x:x;
}
namespace star
{
	const int maxn=35,mod=998244353;
	int n,k,mul[maxn],inv[maxn];
	inline int fpow(int a,int b){int ans=1;for(;b;b>>=1,a=1ll*a*a%mod) if(b&1) ans=1ll*ans*a%mod;return ans;}
	struct poly{
		int a[maxn];
		poly():a(){}
		poly(int x):a(){for(int i=0,d=1;i<=k;i++,d=1ll*d*x%mod) a[i]=1ll*star::inv[i]*d%mod;}
		int& operator [](const int &x){return a[x];}
		const int &operator [](const int &x) const {return a[x];}
		friend poly operator + (const poly& a,const poly& b) {
			poly ans;
			for(int i=0;i<=k;i++) ans[i]=(a[i]+b[i])%mod;
			return ans;
		}
		friend poly operator - (const poly& a,const poly& b) {
			poly ans;
			for(int i=0;i<=k;i++) ans[i]=(a[i]-b[i]+mod)%mod;
			return ans;
		}
		friend poly operator * (const poly& a,const poly& b) {
			poly ans;
			for(int i=0;i<=k;i++) for(int j=0;j<=i;j++) ans[i]=(ans[i]+1ll*a[j]*b[i-j])%mod;
			return ans;
		}
		inline poly operator - () const {
			poly ans;
			for(int i=0;i<=k;i++) ans[i]=(mod-a[i])%mod;
			return ans;
		}
		inline poly inv() const {
			poly ans,res;
			ans[0]=fpow(a[0],mod-2);
			for(int i=1;i<=k;i++) res[i]=1ll*a[i]*ans[0]%mod;
			for(int i=1;i<=k;i++) for(int j=1;j<=i;j++) ans[i]=(ans[i]+1ll*(mod-res[j])*ans[i-j])%mod;
			return ans;
		}
	}a[maxn][maxn],ans;
	inline void work(){
		n=read()-1,k=read();
		mul[0]=inv[0]=1;
		for(int i=1;i<=k;i++) mul[i]=1ll*mul[i-1]*i%mod;
		inv[k]=fpow(mul[k],mod-2);for(int i=k-1;i>0;i--) inv[i]=1ll*inv[i+1]*(i+1)%mod;
		for(int i=0;i<=n;i++) for(int j=0;j<=n;j++) if(i!=j) a[i][j]=-poly(read()),a[i][i]=a[i][i]-a[i][j];else read();
		ans[0]=1;
		for(int i=1;i<=n;i++){
			poly x=a[i][i].inv();
			ans=ans*a[i][i];
			for(int j=i;j<=n;j++) a[i][j]=a[i][j]*x;
			for(int j=1;j<=n;j++) if(j!=i){
				poly res=a[j][i];
				for(int k=i;k<=n;k++) a[j][k]=a[j][k]-a[i][k]*res;
			}
		}
		printf("%lld\n",1ll*ans[k]*mul[k]%mod);
	}
}
signed main(){
	star::work();
	return 0;
}
posted @   Star_Cried  阅读(102)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示