所有生成树边权和的和的求法

通常,矩阵树定理算出的生成树是边权乘积的和。
如果计算所有生成树边权和的和,比较暴力的方法就是枚举一条边,然后计算包含这条边的生成树个数。

这样的时间复杂度是\(O(mn^3)\)的,最坏为\(O(n^3)\)
考虑优化:

对于一条边权为w的边,将边权设为关于x的多项式\(1+wx\)
这样,容易证出,最后的一次项系数就是答案。

把多项式代入高斯消元求值即可。
计算时保留两项即可。

\((a+bx)*(c+dx)=ac+(ad+bc)x\)
\(\frac{1}{a+bx}=\frac{1}{a}-\frac{bx}{a^2}\)

代码:

struct SJd
{
	int a,b;
	SJd(){}
	SJd(int A,int B)
	{
		a=A;b=B;
	}
	SJd(int X)
	{
		a=1;b=X;
	}
};
SJd operator+(SJd x,SJd y)
{
	return SJd((x.a+y.a)%md,(x.b+y.b)%md);
}
SJd operator-(SJd x,SJd y)
{
	return SJd((x.a-y.a+md)%md,(x.b-y.b+md)%md);
}
SJd operator*(SJd x,SJd y)
{
	return SJd(1ll*x.a*y.a%md,(1ll*x.a*y.b+1ll*x.b*y.a)%md);
}
SJd niy(SJd x)
{
	int ny=ksm(x.a,md-2);
	return SJd(ny,(md-1ll*x.b*ny%md*ny%md)%md);
}
int gauss(SJd sz[31][31],int n)
{
	SJd ans(1,0);
	for(int i=0;i<n;i++)
	{
		int wz=-1;
		for(int j=i;j<n;j++)
		{
			if(sz[j][i].a)
			{
				wz=j;
				break;
			}
		}
		if(wz==-1)continue;
		if(wz!=i)
		{
			ans.a=md-ans.a;
			for(int j=i;j<n;j++)
			{
				SJd t=sz[wz][j];
				sz[wz][j]=sz[i][j];
				sz[i][j]=t;
			}
		}
		SJd z=niy(sz[i][i]);
		for(int j=i+1;j<n;j++)
		{
			SJd t=sz[j][i]*z;
			for(re int k=i;k<n;k++)
				sz[j][k]=sz[j][k]-sz[i][k]*t;
		}
	}
	for(int i=0;i<n;i++)
		ans=ans*sz[i][i];
	return ans.b;
}
int A[500],B[500],C[500];SJd sz[31][31];
void addb(int a,int b,int c)
{
	sz[a][a]=sz[a][a]+c;
	sz[a][b]=sz[a][b]-c;
}
//......
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,实现最小生成树的算法可以使用 Prim 或者 Kruskal 算法。这里我给你提供 Kruskal 算法的实现代码,它的时间复杂度为 O(ElogE),其中 E 为边数。 ``` import java.util.*; class Edge implements Comparable<Edge> { int u; int v; int weight; public Edge(int u, int v, int weight) { this.u = u; this.v = v; this.weight = weight; } public int compareTo(Edge other) { return Integer.compare(this.weight, other.weight); } } class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int n = sc.nextInt(); // 顶点数 int m = sc.nextInt(); // 边数 List<Edge> edges = new ArrayList<>(m); for (int i = 0; i < m; i++) { int u = sc.nextInt(); int v = sc.nextInt(); int w = sc.nextInt(); edges.add(new Edge(u, v, w)); } int ans = kruskal(n, edges); System.out.println(ans); } public static int kruskal(int n, List<Edge> edges) { Collections.sort(edges); // 按边权从小到大排序 int[] fa = new int[n + 1]; // 并查集数组 for (int i = 1; i <= n; i++) { fa[i] = i; // 初始化并查集 } int ans = 0; for (Edge e : edges) { int u = e.u; int v = e.v; int w = e.weight; if (find(u, fa) != find(v, fa)) { // 判断 u 和 v 是否在同一个连通块中 fa[find(u, fa)] = find(v, fa); // 将 u 所在的连通块合并到 v 所在的连通块中 ans += w; // 计入边权之和 } } return ans; } public static int find(int x, int[] fa) { if (x != fa[x]) { fa[x] = find(fa[x], fa); // 路径压缩 } return fa[x]; } } ``` 这个程序接受两个整数 n 和 m,表示图的顶点数和边数,以及 m 行整数,每行三个整数 u, v, w,表示一条边从 u 连向 v,边权为 w。程序输出最小生成树边权之和。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值