题目大意
给一个$n\times m$的矩阵,有一些格子不能访问,求可能的生成树个数。
题目分析
生成树计数,矩阵树定理裸题。
定义图的基尔霍夫矩阵为:
矩阵树定理:去掉矩阵任意行与对应的列,剩下的矩阵行列式即为生成树个数。
如何求一个矩阵的行列式?
利用高斯消元将矩阵消为上三角矩阵,主对角线值相乘即为行列式的值。
一般的高斯消元中间过程需要使用到double。
在模意义下的高斯消元可以使用类似辗转相除的方法。
若使用$i$行去消$j$行,对应的值做辗转相除的过程,在辗转相除的过程中消去部分元素。
这样最终一定能消去$i$行或$j$行。
注意:交换任意两行,行列式变为原来的$-1$倍,而给一行同时乘上一个数加到另一行不改变行列式。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
| #include<algorithm> #include<iostream> #include<iomanip> #include<cstring> #include<cstdlib> #include<vector> #include<cstdio> #include<cmath> #include<queue> using namespace std;
typedef long long LL;
inline const int Get_Int() { int num=0,bj=1; char x=getchar(); while(x<'0'||x>'9') { if(x=='-')bj=-1; x=getchar(); } while(x>='0'&&x<='9') { num=num*10+x-'0'; x=getchar(); } return num*bj; }
const LL mod=1e9; const int dirx[]= {1,-1,0,0},diry[]= {0,0,1,-1};
int n,m,map[15][15],id[15][15],bj=1,cnt=0; LL a[105][105],ans=1;
void Simplify(int x) { for(int i=x+1; i<n; i++) { LL A=a[x][x],B=a[i][x]; while(B) { LL t=A/B; A%=B; swap(A,B); for(int j=x; j<n; j++)a[x][j]=(a[x][j]-a[i][j]*t%mod+mod)%mod; for(int j=x; j<n; j++)swap(a[x][j],a[i][j]); bj*=-1; } } ans=ans*a[x][x]%mod; }
int main() { n=Get_Int(); m=Get_Int(); for(int i=1; i<=n; i++) for(int j=1; j<=m; j++) { char x=' '; while(x!='.'&&x!='*')x=getchar(); map[i][j]=x=='.'?1:0; if(map[i][j])id[i][j]=++cnt; } for(int i=1; i<=n; i++) for(int j=1; j<=m; j++) if(map[i][j]) { if(i+1<=n&&map[i+1][j]) { a[id[i][j]][id[i+1][j]]=a[id[i+1][j]][id[i][j]]=-1; a[id[i][j]][id[i][j]]++; a[id[i+1][j]][id[i+1][j]]++; } if(j+1<=m&&map[i][j+1]) { a[id[i][j]][id[i][j+1]]=a[id[i][j+1]][id[i][j]]=-1; a[id[i][j]][id[i][j]]++; a[id[i][j+1]][id[i][j+1]]++; } } n=cnt; for(int i=1; i<=n; i++) for(int j=1; j<=n; j++) a[i][j]=(mod+a[i][j])%mod; for(int i=1; i<n; i++)Simplify(i); ans=(ans*bj+mod)%mod; printf("%lld\n",ans); return 0; }
|