P6178-[模板]Matrix-Tree 定理
正題
題目鏈接: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∏n?Ai,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=1n?Ai,i?。這個很容易理解,因為如果一行選擇的不是iii,那么一定存在有一行jjj的選擇大于jjj,那么此時這個方案的權值就為000了。
那么我們需要將一個矩陣變換成一個行列式不變的上三角矩陣,這里需要用到行列式的初等變換
然后這樣就可以用高斯消元的方法消矩陣了,然后就可以求出行列式了。
回到這題來,矩陣樹定理大體來說就是,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); }總結
以上是生活随笔為你收集整理的P6178-[模板]Matrix-Tree 定理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 电脑一直配置失败还原更改怎么办(电脑一直
- 下一篇: P4336-[SHOI2016]黑暗前的