jzoj5899 【NOIP2018模拟10.6】资源运输 (矩阵树定理)

86 篇文章 0 订阅
19 篇文章 0 订阅

描述

n<=300,给定有权边,求生成树大小和所有生成树边权乘积和。

要点

  • 基尔霍夫矩阵: c [ i ] [ i ] c[i][i] c[i][i]为点i的度数, c [ i ] [ j ] = − ( i , j 之 间 边 数 ) c[i][j]=-(i,j之间边数) c[i][j]=(i,j)
  • 行列式:枚举每一个1…n的排列,将每行对应的列乘起来, 再乘上 ( − 1 ) 逆 序 对 个 数 (-1)^{逆序对个数} (1)之和。
  • PTY式:任意选取i,去掉第i行第i列后的行列式。
  • 基尔霍夫矩阵的余子式就是生成树个数。
  • 有边权视作多条重边即可。

行列式 O ( n 3 ) O(n^3) O(n3)求法:

  • (1)上三角矩阵的行列式就是对角线相乘。
  • (2)为了变成上三角矩阵,高斯消元。
  • (3)高斯消元所需操作:
  • 一行与另一行交换,行列式取相反数。
  • 一行乘a,行列式乘a。
  • 一行减去另一行的倍数:不变。
  • 最好记的性质|AB|=|A||B|,记住这个上面的都可以通过矩阵乘法看出来。
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;

const int N = 310, mo = 998244353;
int n,m;
struct edge{
	int w,x,y;
} e[1010];

ll cnt,sum,f[N][N],g[N][N];
ll ksm(ll x,ll y) {
	ll ret=1; for (;y;y>>=1){
		if (y&1) ret = ret * x % mo;
		x = x * x % mo;
	}
	return ret;
}

void print(ll d[N][N]) {
	for (int i = 1; i < n; i++) {
		for (int j = 1; j < n; j++) printf("%d ",d[i][j]);
		printf("\n");
	}
}

ll solve(ll d[N][N]) {
	int xs = 1;
	for (int i = 1; i < n; i++) {
		for (int j = i; j < n; j++) {
			if (d[j][i]) {
				if (i!=j) xs = -xs;
				for (int z = 1; z < n; z++)
					swap(d[i][z],d[j][z]);
				break;
			}
		}
		for (int j = i+1; j < n; j++) if (d[j][i]) {
			xs = xs * d[i][i] % mo; ll r = d[j][i];
			for (int z = 1; z < n; z++)
				d[j][z] = (d[j][z] * d[i][i] - d[i][z] * r) % mo;
		}
	}
	xs = ksm(xs, mo-2);
	for (int i = 1; i < n; i++) xs = xs * d[i][i] % mo;
	return (xs + mo)  % mo;
}

int main() {
	freopen("avg.in","r",stdin);
//	freopen("avg.out","w",stdout);
	cin>>n>>m;
	for (int i = 1; i <= m; i++) {
		int u,v,w;
		scanf("%d %d %d",&e[i].x,&e[i].y,&e[i].w);
		u = e[i].x, v = e[i].y;
		f[u][u]++;
		f[v][v]++;
		f[u][v]=f[v][u]=-1;
		
		g[u][u]=(g[u][u]+e[i].w)%mo;
		g[v][v]=(g[v][v]+e[i].w)%mo;
		g[u][v]=g[v][u]=-e[i].w;
	}
	ll cnt = solve(f), sum = solve(g);
	cout<<(sum*ksm(cnt,mo-2)%mo+mo)%mo<<endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值