白金元首与独舞 有向图的生成树计数

时间:2020-09-16
本文章向大家介绍白金元首与独舞 有向图的生成树计数,主要包括白金元首与独舞 有向图的生成树计数使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

题目

(白金元首与独舞)https://ac.nowcoder.com/acm/problem/14758

题目描述

元首把花园分为 n 行 m 列的网格。每个格子中都可以放置一个标识,指向上、下、左、右四个方向中的任意一个。元首位于一个格子时,会按照其中标识所指的方向进入周围的格子,或者走出花园(即目的格子不在网格之内)。举个例子 —— 对于下面的放置方式,元首从第 3 行第 2 列的格子开始,会沿着以红色标出的路径走出花园;从第 2 行第 2 列的格子开始,则会在以蓝色标出的环路内不断地行走。

元首已经设计好了大部分格子的标识。元首用字符 L、R、U、D 分别表示指向左、右、上、下四个方向的标识,用字符 . 表示未决定的格子。现在,元首希望将每个 . 替换为 L、R、U、D 中任意一种,使得从花园中的任意一个格子出发,按照上述规则行走,都可以最终走出花园。
你需要编写程序帮助元首计算替换的不同方案数。两个方案不同当且仅当存在一个格子,使得两个方案中该格子内的标识不同。当然,由于答案可能很大,只需给出方案数除以 109+7 所得的余数即可。

输入描述:

输入的第一行包含一个正整数 T —— 测试数据的组数。接下来包含 T 组测试数据,格式如下,测试数据间没有空行。
第 1 行:两个空格分隔的正整数 n、m —— 依次表示花园被分成的行数和列数。
接下来 n 行:每行一个长度为 m 的由字符 L、R、U、D 和 . 组成的字符串 —— 表示花园内已经确定的格子状态。

输出描述:

对于每组测试数据输出一行 —— 满足条件的方案数除以 109 + 7 所得的余数。

输入

5
3 9
LLRRUDUUU
LLR.UDUUU
LLRRUDUUU
4 4
LLRR
L.LL
RR.R
LLRR
4 3
LRD
LUL
DLU
RDL
1 2
LR
2 2
..
..

输出

3
8
0
1
192

说明和备注

说明
第 1 组数据中,将惟一的 . 替换成 R、U 或 D 均满足要求。
第 2 组数据中,将左上方和右下方的两个 . 分别替换成 LR、LU、LD、UR、UU、UD、DR 或 DD 均满足要求。
第 3 组数据中,没有待决定的格子,原本的安排会使得元首陷入无尽的环路,故答案为 0。该组数据与【题目描述】中的例子相同。
第 4 组数据中,也没有待决定的格子,但原本的安排已经满足要求,故答案为 1。
备注:
对于所有数据,有 1≤T≤10,1≤n,m≤200,0≤k≤min(nm,300)。

思路

所有的点都可以到达边界外,如果有环就不可以。

if如果有环:0
else if如果没有'.':1
else{
如果我们把边界外当作一个点1,那么就是一个合格的方案就是
每个点形成了一个1为根的有向生成树。
如果我们直接生成树计数。那么n^3会T。
我们注意到'.'的个数最多300.那么我们考虑'.'为节点。因为除了'.'每个点
的路径一定是确定。而且只能到一个地方。就是树上已经确定的边。所以如果一个'.'可以到达另外一个'.'
那么我们向他们连边。然后有向生成树计数就可以了。
}

#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int mod = 1e9+7;
LL a[310][310];
void gcd(LL a,LL b,LL& x,LL& y) {
    if(!b) {
        x=1;
        y=0;
    } else {
        gcd(b,a%b,y,x);
        y-=x*(a/b);
    }
}
LL inv(LL a) {
    LL x,y;
    gcd(a,mod,x,y);
    return (x%mod+mod)%mod;
}
LL gauss(int n) {
    bool f=0;
    for(int i=1; i<=n; i++)
        for(int j=1; j<=n; j++)
            a[i][j]=(a[i][j]+mod)%mod;
    for(int i=2; i<=n; i++) {
        int r=i;
        for(int j=i+1; j<=n; j++)
            if(a[j][i]>a[r][i])
                r=j;
        if(r!=i) {
            for(int j=i; j<=n+1; j++)
                swap(a[i][j],a[r][j]);
            f^=1;
        }
        LL w=inv(a[i][i]);//
        for(int j=i+1; j<=n; j++) {
            for(int k=n+1; k>=i; k--) {
                a[j][k]=(a[j][k]-1ll*a[j][i]*w%mod*a[i][k]%mod+mod)%mod;//
            }
        }
    }
    LL ans=f?mod-1:1;
    for(int i=2; i<=n; i++)
        ans=1ll*ans*a[i][i]%mod;
    return ans;//
}

char s[310][310];
int vis[310][310];
int xx[]= {0, 1, 0, -1};
int yy[]= {1, 0, -1, 0};
int n, m;
int dfs(int i, int j, int tot) {
    vis[i][j]=tot;
    int k=0;
    if(s[i][j]=='R')
        k=0;
    if(s[i][j]=='D')
        k=1;
    if(s[i][j]=='L')
        k=2;
    if(s[i][j]=='U')
        k=3;
    int x=i+xx[k], y=j+yy[k];
    if(s[x][y]!='.'&&x>=1&&x<=n&&y>=1&&y<=m) {
        if(vis[x][y]!=0&&vis[x][y]!=tot) {
            return 1;
        }
        else if(vis[x][y]==tot){
            return 0;
        }
        else{
            return dfs(x, y, tot);
        }
    }

    return 1;
}
int id[205][205];
int dp[205][205];
int DP(int i, int j) {//记忆化DP每个坐标能够到达的点
    if(dp[i][j]) {
        return dp[i][j];
    }
    if(s[i][j]=='.')
        return dp[i][j]=id[i][j];
    if(!(i>=1&&i<=n&&j>=1&&j<=m)) {
        return dp[i][j]=1;
    }
    int k=0;
    if(s[i][j]=='R')
        k=0;
    if(s[i][j]=='D')
        k=1;
    if(s[i][j]=='L')
        k=2;
    if(s[i][j]=='U')
        k=3;
    int x=i+xx[k], y=j+yy[k];
    if(x>=1&&x<=n&&y>=1&&y<=m) {
        return dp[x][y]=DP(x, y);
    } else {
        return dp[x][y]=1;
    }

}

int main() {

    int T;
    scanf("%d", &T);
    while(T--) {
        scanf("%d%d", &n, &m);
        for(int i=1; i<=n; i++) {
            scanf("%s", s[i]+1);
        }
        memset(vis, 0, sizeof(vis));
        memset(a, 0, sizeof(a));
        memset(dp, 0, sizeof(dp));
        memset(id, 0, sizeof(id));
        int f=0, siz=1, tot=1;
        for(int i=1; i<=n&&!f; i++) {
            for(int j=1; j<=m&&!f; j++) {
                if(s[i][j]!='.'&&dfs(i, j, ++tot)==0) {
                    f=1;
                }
                if(s[i][j]=='.') {//对每个'.'编号
                    ++siz;
                    id[i][j]=siz;
                }
            }
        }
        if(f) {
            printf("%d\n", 0);
            continue;
        } else if(siz==0) {
            printf("1\n");
            continue;
        } else {
            for(int i=1; i<=n; i++) {
                for(int j=1; j<=m; j++) {
                    if(s[i][j]=='.') {
                        int u=id[i][j];
                        for(int k=0; k<4; k++) {
                            int x=i+xx[k], y=j+yy[k];
                            int v=DP(x, y);
                            a[u][v]--, a[u][u]++;
                        }
                    }
                }
            }
            printf("%lld\n", gauss(siz));
        }
    }

    return 0;
}

$flag 上一页 下一页