【CodePlus 2017 12 月赛】白金元首与独舞

时间限制 : 2000 MS 空间限制 : 512 MB

问题描述

【CodePlus 2017 12 月赛】白金元首与独舞_第1张图片

样例输入

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

提示

【CodePlus 2017 12 月赛】白金元首与独舞_第2张图片

题解

显然,原图中有环就是无解的情况。我们把花园外看成一个节点,每个‘.’看成一个节点,然后把各节点按可能的到达关系连边,最后得到一张有向图。那么我们要求的即为以花园外的虚拟点为根的生成树个数。有向图生成树计数方法为:基尔霍夫矩阵只记每个点的出度,最后求其去掉树根后的行列式。

代码

#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int maxn=505;
ll T,n,m,tot,cnt,fa[maxn*maxn],id[maxn][maxn];
ll End[maxn*maxn],Next[maxn*maxn],Last[maxn*maxn];
ll bl[maxn*maxn],G[maxn][maxn];
char s[maxn][maxn];
bool flag;
void Addedge(ll x,ll y)
{
    End[++cnt]=y;
    Next[cnt]=Last[x],Last[x]=cnt;
}
void DFS(ll x)
{
    fa[x]=tot;
    for(ll i=Last[x];i;i=Next[i])
        if(fa[End[i]]<0) DFS(End[i]);
}
ll Gauss(ll n)
{
    ll temp=1,mark=0;
    for(ll i=1;i<=n;i++)
    {
        for(ll j=i+1;j<=n;j++)
            while(G[j][i])
            {
                ll t=G[i][i]/G[j][i];
                for(ll k=i;k<=n;k++) G[i][k]=(G[i][k]-t*G[j][k]%mod+mod)%mod;
                swap(G[i],G[j]),mark^=1;
            }
        temp=temp*G[i][i]%mod;
    }
    if(mark) temp=mod-temp;
    return (temp%mod+mod)%mod;
}
int main()
{
    scanf("%lld",&T);
    while(T--)
    {
        scanf("%lld%lld",&n,&m),tot=cnt=flag=0;
        memset(Last,0,sizeof(Last));
        memset(fa,-1,sizeof(fa));
        memset(id,0,sizeof(id));
        for(ll i=1;i<=n;i++) scanf("%s",s[i]+1);
        for(ll i=1;i<=n;i++)
            for(ll j=1;j<=m;j++) id[i][j]=(i-1)*m+j;
        for(ll i=1;i<=n;i++)
            for(ll j=1;j<=m;j++)
                if(s[i][j]!='.')
                {
                    ll tx=i,ty=j;
                    if(s[i][j]=='U') tx--;
                    else if(s[i][j]=='D') tx++;
                    else if(s[i][j]=='L') ty--;
                    else ty++;
                    Addedge(id[tx][ty],id[i][j]);
                }
        DFS(0);
        for(ll i=1;i<=n;i++)
            for(ll j=1;j<=m;j++)
                if(s[i][j]=='.') tot++,DFS(id[i][j]);
        for(ll i=1;i<=n&&!flag;i++)
            for(ll j=1;j<=m&&!flag;j++)
                if(fa[id[i][j]]<0) flag=true;
        if(flag){puts("0");continue;}
        if(!tot){puts("1");continue;}
        memset(G,0,sizeof(G));
        for(ll i=1;i<=n;i++)
            for(ll j=1;j<=m;j++)
                if(s[i][j]=='.')
                {
                    ll f=fa[id[i][j]];
                    if(fa[id[i-1][j]]!=f) G[f][f]++,G[f][fa[id[i-1][j]]]--;
                    if(fa[id[i][j-1]]!=f) G[f][f]++,G[f][fa[id[i][j-1]]]--;
                    if(fa[id[i+1][j]]!=f) G[f][f]++,G[f][fa[id[i+1][j]]]--;
                    if(fa[id[i][j+1]]!=f) G[f][f]++,G[f][fa[id[i][j+1]]]--;
                }
        printf("%lld\n",Gauss(tot));
    }
    return 0;
}

你可能感兴趣的:(线性代数,--------生成树计数,--------高斯消元)