URAL 1627 (生成树计数)

1627. Join

Time limit: 4.0 second
Memory limit: 64 MB
Businessman Petya recently bought a new house. This house has one floor with  n ×  m  square rooms, placed in rectangular lattice. Some rooms are pantries and the other ones are bedrooms. Now he wants to join all bedrooms with doors in such a way that there will be exactly one way between any pair of them. He can make doors only between neighbouring bedrooms (i.e. bedrooms having a common wall). Now he wants to count the number of different ways he can do it.

Input

First line contains two integers  n and  m  (1 ≤  nm ≤ 9)  — the number of lines and columns in the lattice. Next  n lines contain exactly  m characters representing house map, where "." means bedroom and "*" means pantry. It is guaranteed that there is at least one bedroom in the house.

Output

Output the number of ways to join bedrooms modulo 10 9.

Samples

input output
2 2
..
..
4
2 2
*.
.*
0
Problem Source: SPbSU ITMO contest. Petrozavodsk training camp. Winter 2008.

题意:给出一个图,'.'表示卧室,'*'表示储藏间,每个格子上下左右都有一堵墙,
然后需要打通一些卧室的墙使得卧室之间形成一棵树的方案数.
给每个卧室编个号,给可以打通的卧室加边,就是裸的生成树计数了.
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
#define maxn 111
#define mod 1000000000

#define eps 1e-10
int a[maxn][maxn], g[maxn][maxn];
char mp[maxn][maxn];
int cnt;
int n, m;

long long det ()        //按列化为下三角
{
     long long res = 1;
     for(int i = 0; i < n; ++i)
     {
         if (!a[i][i])
         {
             bool flag = false;
             for (int j = i + 1; j < n; ++j)
             {
                 if (a[j][i])
                 {
                     flag = true;
                     for (int k = i; k < n; ++k)
                     {
                         swap (a[i][k], a[j][k]);
                     }
                     res = -res;
                     break;
                 }
             }

             if (!flag)
             {
                 return 0;
             }
         }

         for (int j = i + 1; j < n; ++j)
         {
             while (a[j][i])
             {
                 long long t = a[i][i] / a[j][i];
                 for (int k = i; k < n; ++k)
                 {
                     a[i][k] = (a[i][k] - t * a[j][k]) % mod;
                     swap (a[i][k], a[j][k]);
                 }
                 res = -res;
             }
          }
          res *= a[i][i];
          res %= mod;
      }
      return (res+mod)%mod;
}

#define move Move
const int move[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};

bool legal (int x, int y) {
    if (x < 0 || y < 0 || x >= n || y >= m)
        return 0;
    return 1;
}

int main()
{
    //freopen("in.txt", "r", stdin);
    while (cin >> n >> m) {
        memset (g, -1, sizeof g);
        cnt = 0;
        memset (a, 0, sizeof a);
        for (int i = 0; i < n; i++) {
            cin >> mp[i];
            for (int j = 0; j < m; j++) {
                if (mp[i][j] == '.')
                    g[i][j] = cnt++;
            }
        }

        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) if (g[i][j] != -1) {
                for (int k = 0; k < 4; k++) {
                    int x = i+move[k][0], y = j+move[k][1];
                    if (!legal (x, y) || g[x][y] == -1)
                        continue;
                    int u = g[i][j], v = g[x][y];
                    a[u][u]++, a[u][v] = -1;
                }
            }
        }
        n = cnt-1;
        cout << det () << "\n";
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值