题目
题目链接: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;
}