1. 程式人生 > >【bzoj5133】[CodePlus2017年12月]白金元首與獨舞 並查集+矩陣樹定理

【bzoj5133】[CodePlus2017年12月]白金元首與獨舞 並查集+矩陣樹定理

oid lin 因此 algorithm fault str typedef 12月 zoj

題目描述

給定一個 $n\times m$ 的方格圖,每個格子有 ↑、↓、←、→,表示從該格子能夠走到相鄰的哪個格子。
有一些格子是空著的,需要填上四者之一,需要滿足:最終的方格圖中,從任意一個位置出發都能夠走出方格圖。求方案數 mod 10^9+7。

$數據組數\le 10$ ,$n,m\le 300$ ,$空格子數k\le 200$


題解

並查集+矩陣樹定理

由於k很小,又是計數問題,考慮矩陣樹定理。

先使用並查集處理出從每個位置開始,最終會走到哪個位置。顯然如果有環則答案為0,否則一定走到的是一個空格子或方格圖外部。

這樣就不用考慮已填好的格子的走法,只需要考慮空格子的走法即可。

每個空格子需要走到方格圖外部,不能有環,相當於是一棵以方格圖外部為根的內向樹形圖。

考慮每個空格子4個方向會走到哪個空格子(或外部),連邊,矩陣樹定理求解即可。

本題要求的是內向樹,因此求 出度矩陣-鄰接矩陣 刪去根節點所在行列,得到的行列式的值 即可。

時間復雜度 $O(nm+k^3)$

#include <cstdio>
#include <cstring>
#include <algorithm>
#define mod 1000000007
using namespace std;
typedef long long ll;
int id[210][210] , f[40010] , flag , v[40010] , wx[310] , wy[310];
ll a[310][310];
char str[210];
inline ll pow(ll x , int y)
{
    ll ans = 1;
    while(y)
    {
        if(y & 1) ans = ans * x % mod;
        x = x * x % mod , y >>= 1;
    }
    return ans;
}
int find(int x)
{
    return x == f[x] ? x : f[x] = find(f[x]);
}
inline void link(int x , int y)
{
    x = find(x) , y = find(y);
    if(x == y) flag = 1;
    f[x] = y;
}
int main()
{
    int T;
    scanf("%d" , &T);
    while(T -- )
    {
        int n , m , p = 0 , i , j , k , d = 0;
        ll t , ans = 1;
        scanf("%d%d" , &n , &m);
        memset(id , 0 , sizeof(id)) , flag = 0;
        for(i = 1 ; i <= n ; i ++ )
            for(j = 1 ; j <= m ; j ++ )
                id[i][j] = (i - 1) * m + j;
        for(i = 0 ; i <= n * m ; i ++ ) f[i] = i;
        for(i = 1 ; i <= n ; i ++ )
        {
            scanf("%s" , str + 1);
            for(j = 1 ; j <= m ; j ++ )
            {
                switch(str[j])
                {
                    case ‘L‘: link(id[i][j] , id[i][j - 1]); break;
                    case ‘R‘: link(id[i][j] , id[i][j + 1]); break;
                    case ‘U‘: link(id[i][j] , id[i - 1][j]); break;
                    case ‘D‘: link(id[i][j] , id[i + 1][j]); break;
                    default: v[id[i][j]] = ++p , wx[p] = i , wy[p] = j;
                }
            }
        }
        if(flag) puts("0");
        else
        {
            memset(a , 0 , sizeof(a));
            for(i = 1 ; i <= p ; i ++ )
            {
                a[i][i] += 4;
                a[i][v[find(id[wx[i]][wy[i] - 1])]] -- ;
                a[i][v[find(id[wx[i]][wy[i] + 1])]] -- ;
                a[i][v[find(id[wx[i] - 1][wy[i]])]] -- ;
                a[i][v[find(id[wx[i] + 1][wy[i]])]] -- ;
            }
            for(i = 1 ; i <= p ; i ++ )
                for(j = 1 ; j <= p ; j ++ )
                    a[i][j] = (a[i][j] + mod) % mod;
            for(i = 1 ; i <= p ; i ++ )
            {
                for(j = i ; j <= p ; j ++ )
                    if(a[i][j])
                        break;
                if(j > p) continue;
                if(j != i)
                {
                    d ^= 1;
                    for(k = i ; k <= p ; k ++ )
                        swap(a[i][k] , a[j][k]);
                }
                ans = ans * a[i][i] % mod;
                t = pow(a[i][i] , mod - 2);
                for(j = i ; j <= p ; j ++ ) a[i][j] = a[i][j] * t % mod;
                for(j = i + 1 ; j <= p ; j ++ )
                    for(t = a[j][i] , k = i ; k <= p ; k ++ )
                        a[j][k] = (a[j][k] - a[i][k] * t % mod + mod) % mod;
            }
            for(i = 1 ; i <= p ; i ++ ) ans = ans * a[i][i] % mod;
            if(d) ans = (mod - ans) % mod;
            printf("%lld\n" , ans);
        }
    }
    return 0;
}

【bzoj5133】[CodePlus2017年12月]白金元首與獨舞 並查集+矩陣樹定理