【洛谷P6178】【模板】Matrix-Tree 定理

题目

题目链接:https://www.luogu.com.cn/problem/solution/P6178
给定一张 (n) 个结点 (m) 条边的带权图(可能为无向图,可能为有向图)。
定义其一个生成树 (T) 的权值为 (T) 中所有边权的乘积。
求其所有不同生成树的权值之和,对 (10^9+7) 取模。

思路

一般的矩阵树定理只能处理生成树数量的情况,但是这道题要求我们输出所有生成树权值之和。
我们可以把一条边 ((x,y,z)) 拆成 (z)((x,y,1)) 的边,这样原来的一棵生成树,假设其权值为 (k),就变成了 (k) 棵生成树。此时只要求生成树数量即可。
对于有向边的情况,如果要求外向树,那么一条边 ((x,y)) 能贡献的就只有 (g[y][y])(g[x][y])。证明不会。
注意需要把自环去掉。
时间复杂度 (O(n^3))

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N=310,MOD=1e9+7;
int n,m,type;
ll g[N][N];

ll fpow(ll x,ll k)
{
	ll ans=1;
	for (;k;k>>=1,x=x*x%MOD)
		if (k&1) ans=ans*x%MOD;
	return ans;
}

ll dit()
{
	ll f=1,res=1;
	for (int i=1;i<=n;i++)
		for (int j=1;j<=n;j++)
			g[i][j]=(g[i][j]%MOD+MOD)%MOD;
	for (int i=2;i<=n;i++)
	{
		for (int j=i;j<=n;j++)
			if (g[j][i])
			{
				if (j!=i) f=-f;
				for (int k=2;k<=n;k++)
					swap(g[j][k],g[i][k]);
				break;
			}
		ll inv=fpow(g[i][i],MOD-2);
		for (int j=i+1;j<=n;j++)
			if (g[j][i])
			{
				ll base=inv*g[j][i]%MOD;
				for (int k=1;k<=n;k++)
					g[j][k]=((g[j][k]-base*g[i][k])%MOD+MOD)%MOD;
			}
	}
	for (int i=2;i<=n;i++) res=res*g[i][i]%MOD;
	return (res*f%MOD+MOD)%MOD;
}

int main()
{
	scanf("%d%d%d",&n,&m,&type);
	for (int i=1,x,y,z;i<=m;i++)
	{
		scanf("%d%d%d",&x,&y,&z);
		if (x==y) continue;
		if (type)
			g[x][y]-=z,g[y][y]+=z;
		else
			g[x][y]-=z,g[y][x]-=z,g[x][x]+=z,g[y][y]+=z;
	}
	printf("%lld",dit());
	return 0;
}
原文地址:https://www.cnblogs.com/stoorz/p/14233784.html