给定一个 n 个点 m 条边的带权有向图,每条边描述为 (ui,vi,wi) 。不存在自环,可能有重边。
请计算出所有的以 n 为根的有向生成树(在本题定义为所有边从儿子指向父亲)的权值和,一棵树的权值定义为其所有边的权值和。
2≤n≤300,0≤m≤105,1≤ui,vi≤n,1≤wi≤109
在我的Matrix-Tree定理学习小记(详细介绍+证明)中你可以找到基尔霍夫定理在有向图上的推广。
如果只是求方案数的话直接有向图基尔霍夫矩阵树定理做,求出 Mnn 就好了,可是这题里面我们要计算权值和。
怎么办呢?我们定义一种新的数 (a,b) ,它代表了权值和为 b 的一个局部生成树有 a 种方案。定义其加法为直接相加,表示两种互相独立的方案,减法是加法逆运算;乘法为 (ab,ad+bc) ,意义为两种方案的合并。定义其除法为乘法的逆运算,即 (ac,bc−adc2) 。单位元为 (1,0) ,零元为 (0,0) 。
然后用这种新的数代替原本的数做基尔霍夫定理就好了。加入一条边直接在邻接矩阵对应位置加上 (1,wi) ,度数矩阵类似。消元过程和普通的数的运算一致,要把下三角消成零元。然后答案就是行列式得出来的数的第二项。
时间复杂度 O(n3) 。
#include
#include
#include
#include
#include
using namespace std;
int read()
{
int x=0,f=1;
char ch=getchar();
while (!isdigit(ch)) f=ch=='-'?-1:f,ch=getchar();
while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();
return x*f;
}
const int P=1000000007;
const int N=305;
typedef pair<int,int> PI;
#define mkp(a,b) make_pair(a,b)
#define ft first
#define sd second
int sqr(int x){return 1ll*x*x%P;}
int quick_power(int x,int y)
{
int ret=1;
for (;y;y>>=1,x=1ll*x*x%P) if (y&1) ret=1ll*ret*x%P;
return ret;
}
PI operator+(PI x,PI y){return mkp((x.ft+y.ft)%P,(x.sd+y.sd)%P);}
PI operator+=(PI &x,PI y){return x=x+y;}
PI operator-(PI x){return mkp(P-x.ft,P-x.sd);}
PI operator-(PI x,PI y){return x+(-y);}
PI operator-=(PI &x,PI y){return x=x-y;}
PI operator*(PI x,PI y){return mkp(1ll*x.ft*y.ft%P,(1ll*x.ft*y.sd%P+1ll*x.sd*y.ft%P)%P);}
PI operator*=(PI &x,PI y){return x=x*y;}
PI operator/(PI x,PI y)
{
int inv=quick_power(y.ft,P-2);
return mkp(1ll*x.ft*inv%P,1ll*(1ll*x.sd*y.ft%P-1ll*x.ft*y.sd%P+P)%P*sqr(inv)%P);
}
PI tmp[N][N];
struct matrix
{
PI num[N][N];
int s;
PI det()
{
memcpy(tmp,num,sizeof num);bool sig=1;
for (int i=0;i<s;++i)
{
if (!tmp[i][i].ft)
for (int j=i+1;j<s;++j)
if (tmp[j][i].ft)
{
sig^=1;
for (int k=i;k<s;++k) swap(tmp[i][k],tmp[j][k]);
break;
}
if (!tmp[i][i].ft) continue;
PI inv=mkp(1,0)/tmp[i][i],mul;
for (int j=i+1;j<s;++j)
if (tmp[j][i].ft)
{
mul=tmp[j][i]*inv;
for (int k=i;k<s;++k) tmp[j][k]-=tmp[i][k]*mul;
}
}
PI ret=mkp(1,0);
for (int i=0;i<s;++i) ret*=tmp[i][i];
return sig?ret:-ret;
}
}Kir;
int n,m;
int main()
{
freopen("calc.in","r",stdin),freopen("calc.out","w",stdout);
n=read(),m=read(),Kir.s=n;
for (int i=1,x,y,z;i<=m;++i)
{
x=read()-1,y=read()-1,z=read(),swap(x,y);
Kir.num[y][y]+=mkp(1,z),Kir.num[x][y]-=mkp(1,z);
}
--Kir.s,printf("%d\n",Kir.det().sd);
fclose(stdin),fclose(stdout);
return 0;
}