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

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

这样的时间复杂度是\(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;
}
//......
posted @ 2020-06-24 22:54  lnzwz  阅读(773)  评论(0编辑  收藏  举报