zoukankan      html  css  js  c++  java
  • 【HEOI2015】小Z的房间

     题意

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

     题解

    前置知识:矩阵树定理

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

     

    因为矩阵树定理就是求一张 $n$ 个点的简单无向图的生成树个数,时间复杂度为 $O(n^3)$,再看看这道题的数据范围,$n,mle 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
  • 相关阅读:
    flutter canvas 简单绘画直线
    Yapi 部署及遇到的坑
    flutter 时间选择器第三方插件返回时间格式说明
    windows + flutter + vscode 连接其他模拟器
    flutter 配置环境
    flutter 返回键监听
    flutter 自定义主题切换
    flutter Provide 状态管理篇
    flutter 本地存储 (shared_preferences)
    JS做倒计时的例题
  • 原文地址:https://www.cnblogs.com/scx2015noip-as-php/p/bzoj4031.html
Copyright © 2011-2022 走看看