【HEOI2015】小Z的房间

 题意

https://www.luogu.org/problemnew/show/P4111

 题解

前置知识:矩阵树定理

不要问证明,我不会,用就完事了(反正一般也不会用到)

 

因为矩阵树定理就是求一张 $n$ 个点的简单无向图的生成树个数,时间复杂度为 $O(n^3)$,再看看这道题的数据范围,$n,m\le 9$,直接矩阵树定理就可以了……

注意因为我们要求方案数,这个数可能很大,而我们用高斯消元把矩阵消成上三角的话,由于要用 $double$,会出现精度误差,这种小数运算的误差在这种求方案数的题中是不允许的(因为方案数就是一个准确的整数,没有保留几位小数之说)。所以这里采用辗转相除法把高斯消元的非整数倍消元 转成多次整数倍消元,具体实现见代码,就是对矩阵的两行进行类似于辗转相除的操作。总时间复杂度就在普通 $O(n^3)$ 高消基础上 乘上每次辗转相除的复杂度(辗转相除的复杂度是 $O(辗转相除的两数中较小数在斐波那契数列的第几项)$)。

upd:时间复杂度应该是 $O(n^3+n^2 P)$($P$ 是模数,即值域),而不是网上大部分题解说的 $O(n^3 \log{P})$。证明(因为辗转相除的次数接近 $log$,我们可以近似地用 $log$ 表示辗转相除的复杂度):

 

 1 #include<bits/stdc++.h>
 2 #define ll long long
 3 #define N 110
 4 #define p 1000000000
 5 using namespace std;
 6 const int dx[2] = {-1, 0}, dy[2] = {0, -1};
 7 inline int read(){
 8     int x = 0; bool f = 1; char c = getchar();
 9     for(; !isdigit(c); c=getchar()) if(c == '-') f = 0;
10     for(;  isdigit(c); c=getchar()) x = (x<<3) + (x<<1) + (c^'0');
11     if(f) return x;
12     return 0 - x;
13 }
14 int n, m, tot, id[N][N];
15 bool e[N][N];
16 ll d[N][N];
17 char s[N];
18 ll solve(){
19     int n = tot - 1;
20     bool tr = 0;
21     ll ans = 1;
22     
23     for(int i=1; i<=n; ++i){
24         for(int j=i+1; j<=n; ++j){
25             while(d[j][i]){
26                 ll tmp = d[i][i] / d[j][i];
27                 for(int k=1; k<=n; ++k){ //辗转相除 
28                     d[i][k] = (d[i][k] - tmp * d[j][k] % p + p) % p;
29                     swap(d[i][k], d[j][k]);
30                 }
31                 tr ^= 1;
32             }
33         }
34         if(!d[i][i]) return 0;
35         ans = ans * d[i][i] % p;
36     }
37     if(tr) ans = p - ans; //原型是 ans = -ans,交换奇数次行时,行列式值要取负 
38     return ans;
39 }
40 int main(){
41     n = read(), m = read();
42     for(int i=1; i<=n; ++i){
43         scanf("%s", s+1);
44         for(int j=1; j<=m; ++j)
45             if(s[j] == '.') e[i][j] = 1;
46     }
47     for(int i=1; i<=n; ++i){
48         for(int j=1; j<=m; ++j){
49             if(!e[i][j]) continue;
50             id[i][j] = ++tot;
51             int u = id[i][j];
52             for(int k=0; k<2; ++k){
53                 int x = i + dx[k], y = j + dy[k];
54                 if(!e[x][y]) continue;
55                 int v = id[x][y];
56                 ++d[u][u], ++d[v][v],
57                 d[u][v] = (d[u][v] - 1 + p) % p, d[v][u] = (d[v][u] - 1 + p) % p;
58             }
59         }
60     }
61     cout << solve() << endl; 
62     return 0;
63 }
View Code

 

posted @ 2019-07-10 16:00  大本营  阅读(278)  评论(0编辑  收藏  举报