正题
题目链接:https://www.luogu.com.cn/problem/P6178
题目大意
给出一个nnn个点mmm条边的无向/有向图。
求所有的生成树/以1为根的外向生成树的权值乘积和。
解题思路
矩阵AAA的行列式表示为det(A)det(A)det(A),定义为
det(A)=∑P(−1)μ(P)∏i=1nAi,pidet(A)=\sum_P(-1)^{\mu(P)}\prod_{i=1}^nA_{i,p_i}det(A)=P∑(−1)μ(P)i=1∏nAi,pi
其中PPP是一个1∼n1\sim n1∼n的排列,μ(P)\mu(P)μ(P)表示排列PPP的逆序对数量
即每一行选择一个数乘起来,容斥系数与排列逆序对数量有关
我们需要解决的问题是如何快速求出一个矩阵的行列式,考虑如果对于所有行iii都满足i+1∼ni+1\sim ni+1∼n都没有值,那么此时有det(A)=∏i=1nAi,idet(A)=\prod_{i=1}^nA_{i,i}det(A)=∏i=1nAi,i。这个很容易理解,因为如果一行选择的不是iii,那么一定存在有一行jjj的选择大于jjj,那么此时这个方案的权值就为000了。
那么我们需要将一个矩阵变换成一个行列式不变的上三角矩阵,这里需要用到行列式的初等变换
- 若交换两行,那么行列式。因为交换后原来权值相同的容斥系数都会变化,所以值取反。
- [a+x,b+yc,d]=[a,bc,d]+[x,yc,d]\begin{bmatrix}a+x,b+y\\c,d\end{bmatrix}=\begin{bmatrix}a,b\\c,d\end{bmatrix}+\begin{bmatrix}x,y\\c,d\end{bmatrix}[a+x,b+yc,d]=[a,bc,d]+[x,yc,d]乘法分配率可得
- 若有两行完全相同,那么这个行列式为000。证明方法是交换这两行后矩阵不变理论上行列式也不便,但是行列式取反了,也就是原行列式为000
- 若让一行加上另一行那么行列式不变,证明的话[a,ba,b]=0\begin{bmatrix}a,b\\a,b\end{bmatrix}=0[a,ba,b]=0 [a,ba+c,b+d]=[a,ba,b]+[a,bc,d]=[a,bc,d]\begin{bmatrix}a,b\\a+c,b+d\end{bmatrix}=\begin{bmatrix}a,b\\a,b\end{bmatrix}+\begin{bmatrix}a,b\\c,d\end{bmatrix}=\begin{bmatrix}a,b\\c,d\end{bmatrix}[a,ba+c,b+d]=[a,ba,b]+[a,bc,d]=[a,bc,d]
然后这样就可以用高斯消元的方法消矩阵了,然后就可以求出行列式了。
回到这题来,矩阵树定理大体来说就是,AAA表示一张无向图的邻接矩阵,DDD表示度数矩阵(Di,iD_{i,i}Di,i为iii的度数,其他为000)。去掉DDD和AAA的第kkk行和第kkk列之后就有det(D−A)det(D-A)det(D−A)就表示这张图的生成树个数。
当然有向图也可以,如果是外向树,那么DDD表示入度矩阵,内向就是出度矩阵,若xxx为根,去掉第xxx行第xxx列之后det(D−A)det(D-A)det(D−A)就是答案了。
这里是求权值乘积和,重边数等于权值就好了。
时间复杂度O(n3)O(n^3)O(n3)
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=310,P=1e9+7;
ll n,m,t,ans,a[N][N];
ll power(ll x,ll b){ll ans=1;while(b){if(b&1)ans=ans*x%P;x=x*x%P;b>>=1;}return ans;
}
void dec(){ll f=1;ans=1;for(ll i=2;i<=n;i++){ll w=i;for(ll j=i;j<=n;j++)if(a[i][j]){if(i!=j)f=-f;w=j;break;}swap(a[i],a[w]);ll inv=power(a[i][i],P-2);ans=ans*a[i][i]%P;if(!a[i][i])return;for(ll j=i;j<=n;j++)a[i][j]=a[i][j]*inv%P;for(ll j=i+1;j<=n;j++){ll rate=P-a[j][i];for(ll k=i;k<=n;k++)a[j][k]=(a[j][k]+rate*a[i][k]%P+P)%P;}}ans=ans*f;return;
}
int main()
{scanf("%lld%lld%lld",&n,&m,&t);for(ll i=1;i<=m;i++){ll x,y,w;scanf("%lld%lld%lld",&x,&y,&w);if(t)a[y][y]=(a[y][y]+w)%P,a[x][y]=(a[x][y]-w)%P;else a[x][x]=(a[x][x]+w)%P,a[y][y]=(a[y][y]+w)%P,a[x][y]=(a[x][y]-w)%P,a[y][x]=(a[y][x]-w)%P;}dec();printf("%lld\n",(ans+P)%P);
}