1. 程式人生 > >矩陣樹定理(Matrix Tree)學習筆記

矩陣樹定理(Matrix Tree)學習筆記

cstring 相關 () str eof bsp lld open csdn

如果不談證明,稍微有點線代基礎的人都可以在兩分鐘內學完所有相關內容。。

行列式隨便找本線代書看一下基本性質就好了。

學習資源:

https://www.cnblogs.com/candy99/p/6420935.html

http://blog.csdn.net/Marco_L_T/article/details/72888138

首先是行列式對幾個性質(基本上都是用數學歸納法證):

1.交換兩行(列),行列式取相反數

2.由1.得若存在兩行(列)完全相同則行列式為0

3.上(下)三角行列式即主對角線值之積

只有這三條用得上。

然後就可以直接上Matrix Tree定理了:一個無向圖的鄰接矩陣減去度數矩陣得到的矩陣的任意n-1階子式的行列式的絕對值等於其有標號生成樹的數目。

其中鄰接矩陣-度數矩陣即為基爾霍夫矩陣(又稱拉普拉斯算子)。

加強版:有向圖的樹形圖(從根可以走到任意點)個數,將上面的“度數矩陣”改為“入度矩陣即可”(見第二份鏈接)

關於行列式的算法,按定義樸素跑復雜度是階乘級別的,利用上面的性質,初等行變換使之成為上三角矩陣即可。(實際上就是高斯消元)。註意高消中基準元要選絕對值最大的,以及註意判0退出。

下面是幾道裸題:

SPOJ Highways 裸題

 1 #include<cmath>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5
#define rep(i,l,r) for (int i=l; i<=r; i++) 6 using namespace std; 7 8 const int N=15; 9 const double eps=1e-8; 10 11 int T,n,m,u,v; 12 double a[N][N]; 13 14 void Gauss(){ 15 n--; 16 rep(i,1,n){ 17 int r=i; 18 rep(j,i+1,n) if (fabs(a[j][i])>fabs(a[r][i])) r=j; 19
if (fabs(a[r][i]<eps)) { puts("0"); return; } 20 if (r!=i) rep(k,1,n) swap(a[r][k],a[i][k]); 21 rep(j,i+1,n){ 22 double t=a[j][i]/a[i][i]; 23 rep(k,i,n) a[j][k]-=t*a[i][k]; 24 } 25 } 26 double ans=1; 27 rep(i,1,n) ans*=a[i][i]; 28 printf("%.0f\n",abs(ans)); 29 } 30 31 int main(){ 32 for (scanf("%d",&T); T--; ){ 33 scanf("%d%d",&n,&m); 34 memset(a,0,sizeof(a)); 35 rep(i,1,m) scanf("%d%d",&u,&v),a[u][u]++,a[v][v]++,a[u][v]--,a[v][u]--; 36 Gauss(); 37 } 38 return 0; 39 }

BZOJ4766:

先手工構造出矩陣然後觀察規律求出公式。

https://blog.sengxian.com/solutions/bzoj-4766

 1 #include<cstdio>
 2 #include<algorithm>
 3 using namespace std;
 4 typedef long long ll;
 5 
 6 ll n,m,P;
 7 ll mod(ll x){ return (x<P) ? x : x-P; }
 8 
 9 ll mul(ll a,ll b){
10     ll res=0;
11     for (; b; b>>=1,a=mod(a+a))
12         if (b & 1) res=mod(res+a);
13     return res;
14 }
15 
16 ll pow(ll a,ll b){
17     ll res=1;
18     for (; b; b>>=1,a=mul(a,a))
19         if (b & 1) res=mul(res,a);
20     return res;
21 }
22 
23 int main(){
24     scanf("%lld%lld%lld",&n,&m,&P);
25     printf("%lld\n",mul(pow(n,m-1),pow(m,n-1)));
26     return 0;
27 }

BZOJ4031 裸題

這裏又個trick,高斯消元的時候因為模數是個合數不好求逆元,所以用輾轉相除的方法做就好了。

就是不停對兩行相互進行初等行變換直到其中一行第一個數變為0,這樣復雜度是log的,並且保證膜意義下不會出錯。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #define rep(i,l,r) for (int i=l; i<=r; i++)
 5 typedef long long ll;
 6 using namespace std;
 7 
 8 const int N=105,md=1000000000;
 9 int n,m,a[N][N],id[N][N],tot;
10 char s[N][N];
11 
12 void Gauss(int n){
13     int s=0;
14     rep(i,1,n){
15         int r=i;
16         rep(j,i+1,n) if (a[j][i]>a[r][i]) r=j;
17         if (!a[r][i]) { puts("0"); return; }
18         if (i!=r){
19             s^=1;
20             rep(k,i,n) swap(a[i][k],a[r][k]);
21         }
22         rep(j,i+1,n){
23             while (a[j][i]){
24                 ll t=a[j][i]/a[i][i];
25                 rep(k,i,n) a[j][k]=(a[j][k]-t*a[i][k]%md+md)%md;
26                 if (!a[j][i]) break;
27                 s^=1;
28                 rep(k,i,n) swap(a[i][k],a[j][k]);
29             }
30         }
31     }
32     ll ans=1;
33     rep(i,1,n) ans=ans*a[i][i]%md;
34     if (s) ans=(md-ans)%md;
35     printf("%lld\n",ans);
36 }
37 
38 void work(){
39     rep(i,1,m) rep(j,1,n) if (s[i][j]==.) id[i][j]=++tot;
40     rep(i,1,m) rep(j,1,n) if (id[i][j]){
41         int u=id[i][j],v;
42         if (i!=1 && s[i-1][j]==.)
43             v=id[i-1][j],a[u][u]++,a[v][v]++,a[u][v]--,a[v][u]--;
44         if (j!=1 && s[i][j-1]==.)
45             v=id[i][j-1],a[u][u]++,a[v][v]++,a[u][v]--,a[v][u]--;
46     }
47     rep(i,1,m*n) rep(j,1,m*n) a[i][j]=(a[i][j]+md)%md;
48 }
49 
50 int main(){
51     freopen("bzoj4031.in","r",stdin);
52     freopen("bzoj4031.out","w",stdout);
53     scanf("%d%d",&m,&n);
54     rep(i,1,m) scanf("%s",s[i]+1);
55     work(); Gauss(tot-1);
56     return 0;
57 }

矩陣樹定理(Matrix Tree)學習筆記