所有生成树边权和的和的求法
通常,矩阵树定理算出的生成树是边权乘积的和。
如果计算所有生成树边权和的和,比较暴力的方法就是枚举一条边,然后计算包含这条边的生成树个数。
这样的时间复杂度是\(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;
}
//......