【JZOJ 5153】树形图求和

题目

Description
在这里插入图片描述

Input
在这里插入图片描述

Output
在这里插入图片描述

Sample Input
见附加文件

Sample Output
见附加文件

Data Constraint
在这里插入图片描述

思路

矩阵树定理
我们考虑分别计算每一条边对答案的贡献;即需要对于每一条边得知包含它的树形图个数有多少个。
对于树形图计数,可以考虑用基尔霍夫矩阵求解。于是朴素的想法就是对于每一条边,将它相连的两个端点合并成一个点,然后求出树形图个数以更新答案。
事实上,包含一条边的树形图个数,就是总共的树形图个数减去不包含这条边的树形图个数。而不包含这条边的树形图个数,就相当于在基尔霍夫矩阵某一行修改两个值后的矩阵的行列式。

于是这道题就很简单了

代码

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

const int mod = 1e9 + 7;

ll power(ll x,ll y) 
{
	ll b=1;
	while(y)
	{
		if(y&1) b=b*x%mod;
		x=x*x%mod; y>>=1;
	}
	return b;
}

int n,m;

struct nod 
{
	int x,y,z;	
} b[100005];

const int N = 305;

ll a[N][N],c[N][N];

ll yjy;

void work(ll (*a)[N],ll (*b)[N],int n) 
{
	yjy = 1;
	for(int i=1; i<=n; i++) b[i][i] = 1;
	for(int i=1; i<=n; i++)
	{
		int u = -1;
		for(int j=i; j<=n; j++) if(a[j][i]) 
		{
			u=j; break;
		}
		if(u==-1) 
		{
			yjy=0; return;
		}
		if(u!=i) 
		{
			for(int k=1; k<=n; k++) swap(a[i][k],a[u][k]),swap(b[i][k],b[u][k]);
			yjy*=-1;
		}
		ll v=power(a[i][i],mod-2);
		yjy=yjy*a[i][i]%mod;
		for(int k=1; k<=n; k++) a[i][k]=a[i][k]*v%mod,b[i][k]=b[i][k]*v%mod;
		for(int j=1; j<=n; j++) if(i!=j&&a[j][i]) 
		{
			ll v=a[j][i];
			for(int k=1; k<=n; k++) a[j][k]=(a[j][k]-a[i][k]*v)%mod,b[j][k]=(b[j][k]-b[i][k]*v)%mod;
		}
	}
}

int main() 
{
//	freopen("calc.in","r",stdin); freopen("calc.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(int i=1; i<=m; i++)
	{
		scanf("%d%d%d",&b[i].x,&b[i].y,&b[i].z);
		a[b[i].x][b[i].x]++;
		a[b[i].x][b[i].y]--;
	}
	work(a,c,n-1);
	ll ans=0;
	for(int i=1; i<=m; i++)
	{
		int x=b[i].x;
		if(x==n) continue;
		ans=(ans+(yjy*(c[b[i].x][x]-c[b[i].y][x]))%mod*b[i].z)%mod;
	}
	ans=(ans%mod+mod)%mod;
	printf("%lld\n",ans);
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章