1. 程式人生 > >走進矩陣樹定理--「CodePlus 2017 12 月賽」白金元首與獨舞

走進矩陣樹定理--「CodePlus 2017 12 月賽」白金元首與獨舞

生成樹 isp 多余 矩陣樹定理 遇到 每次 ans ace 指向

n,m<=200,n*m的方陣,有ULRD表示在這個格子時下一步要走到哪裏,有一些待決策的格子用.表示,可以填ULRD任意一個,問有多少種填法使得從每個格子出發都能走出這個方陣,答案取模。保證未確定的格子<=300。

。。。一臉懵逼地寫了原本30實際20的暴力然後跪著啃了下論文

然而什麽都沒啃懂不如結論記下來:

首先矩陣行列式的定義:一個n*n的矩陣,行列式值為$\sum_{b是n的一個排列} \ \ \ \ \ ( (-1)^{b的逆序對數} \ \ \ \ \ * \prod_{i=1}^{n} A_{i,b_i})$

矩陣A行列式的性質:

|A|=|A的轉置|

|AB|=|A||B|

|A|+|B|=|A和B的某一行加起來其他不變的矩陣|

交換兩行得B,|B|=-|A|,因為每次計算一個排列時逆序對數都相差1。

A的某行乘個x得B,|B|=x|A|。

A的某行乘某個數加到另一行上得B,|B|=|A|。由第三條可得。

每行每列和為0的矩陣行列式為0。

由四五六條可用高斯消元計算行列式。

基爾霍夫矩陣:

無向圖:矩陣對角線上是度數,其他如果對應邊存在就是-1,不然就是0。

有向圖:矩陣對角線上是入度,其他如果對應邊存在就是-1,不然就是0。

矩陣樹定理:一個無向圖的基爾霍夫矩陣的任意一個n-1階的子矩陣,即刪掉了第i行和第i列,的行列式是這個圖的生成樹個數。

一個有向圖以點i為根的生成樹個數是基爾霍夫矩陣去掉第i行和第i列剩下的矩陣的行列式。

嗯然後就是這道題。

首先,如果在外界虛擬一個節點,把所有指出去的格子都指向它,把所有確定格子向指向的點連邊,可得一個有向圖。

然後,待確定的點向四個方向都連邊,求這個圖以外界點為根的反向的樹形圖即可。為什麽呢,首先確定的格子的邊一定會選到,因為每個格子只有一條出邊,不然他就和其他點斷掉了;其次那些待確定的格子為了形成樹,只會在四個方向裏選一個。

嗯這樣只能拿50分。

可以發現那些已經確定的點是多余的,可以縮掉。也就是給所有待確定格子編號,外界點編號0,然後預處理他往上下左右走能遇到的第一個有編號的格子,朝他們連邊即可。

然後就大功告成了。

技術分享圖片
  1 #include<stdio.h>
  2
#include<string.h> 3 #include<algorithm> 4 #include<stdlib.h> 5 //#include<queue> 6 #include<math.h> 7 //#include<time.h> 8 //#include<iostream> 9 using namespace std; 10 11 int n,m,K,T; 12 const int mod=1e9+7; 13 #define maxn 311 14 int pos[maxn][maxn],who[maxn][maxn],place[maxn][4]; char mp[maxn][maxn]; 15 16 int powmod(int a,int b) 17 { 18 int ans=1; 19 while (b) 20 { 21 if (b&1) ans=1ll*ans*a%mod; 22 a=1ll*a*a%mod; 23 b>>=1; 24 } 25 return ans; 26 } 27 28 struct Mat 29 { 30 int num[maxn][maxn]; 31 void clear() {memset(num,0,sizeof(num));} 32 }mat; 33 int gauss() 34 { 35 int ans=1; 36 for (int i=1;i<=K;i++) 37 { 38 int id=i; 39 for (int j=i+1;j<=K;j++) if (fabs(mat.num[j][i])>fabs(mat.num[id][i])) id=j; 40 if (id!=i) 41 { 42 ans=1ll*ans*(mod-1)%mod; 43 for (int j=i;j<=K;j++) {int t=mat.num[i][j]; mat.num[i][j]=mat.num[id][j]; mat.num[id][j]=t;} 44 } 45 int tmp=powmod(mat.num[i][i],mod-2); 46 for (int j=i+1;j<=K;j++) 47 for (int k=K;k>=i;k--) 48 mat.num[j][k]-=mat.num[i][k]*1ll*mat.num[j][i]%mod*tmp%mod, 49 mat.num[j][k]+=mat.num[j][k]<0?mod:0; 50 } 51 for (int i=1;i<=K;i++) ans=1ll*ans*mat.num[i][i]%mod; 52 return ans; 53 } 54 55 int vis[maxn][maxn],Time; bool die; 56 void dfs(int x,int y) 57 { 58 if (mp[x][y]==.) return; 59 if (vis[x][y]) 60 { 61 if (pos[x][y]==-1) die=1; 62 return; 63 } 64 vis[x][y]=1; 65 int tx=x,ty=y; 66 if (mp[x][y]==U) x--; 67 else if (mp[x][y]==D) x++; 68 else if (mp[x][y]==L) y--; 69 else if (mp[x][y]==R) y++; 70 if (x<1 || x>n || y<1 || y>m) pos[tx][ty]=0; 71 else pos[tx][ty]=-1,dfs(x,y),pos[tx][ty]=pos[x][y]; 72 } 73 int check(int x,int y) 74 { 75 if (x<1 || x>n || y<1 || y>m) return 0; 76 return pos[x][y]; 77 } 78 79 int main() 80 { 81 scanf("%d",&T); 82 while (T--) 83 { 84 scanf("%d%d",&n,&m); K=0; Time=0; die=0; 85 memset(vis,0,sizeof(vis)); 86 memset(pos,0,sizeof(pos)); 87 for (int i=1;i<=n;i++) scanf("%s",mp[i]+1); 88 for (int i=1;i<=n;i++) 89 for (int j=1;j<=m;j++) 90 if (mp[i][j]==.) pos[i][j]=++K; 91 for (int i=1;i<=n;i++) 92 for (int j=1;j<=m;j++) 93 if (mp[i][j]!=.) dfs(i,j); 94 if (die) {puts("0"); continue;} 95 96 for (int i=1;i<=n;i++) 97 for (int j=1;j<=m;j++) 98 if (mp[i][j]==.) 99 { 100 int nx=i,ny=j,now=pos[i][j]; 101 nx++; place[now][0]=check(nx,ny); nx--; 102 nx--; place[now][1]=check(nx,ny); nx++; 103 ny++; place[now][2]=check(nx,ny); ny--; 104 ny--; place[now][3]=check(nx,ny); ny++; 105 } 106 mat.clear(); 107 for (int i=1;i<=K;i++) 108 { 109 for (int j=0;j<4;j++) mat.num[place[i][j]][i]--; 110 mat.num[i][i]+=4; 111 } 112 for (int i=1;i<=K;i++) 113 for (int j=1;j<=K;j++) 114 if (mat.num[i][j]<0) mat.num[i][j]+=mod; 115 printf("%d\n",gauss()); 116 } 117 return 0; 118 }
View Code

走進矩陣樹定理--「CodePlus 2017 12 月賽」白金元首與獨舞