【BZOJ5133】[CodePlus2017年12月]白金元首与独舞
题面:www.lydsy.com/JudgeOnline/upload/201712/div1.pdf
题解:由于k很小,考虑用矩阵树定理。
我们先预处理出:从每个已决策点,一直走下去会走到哪个未决策点(我们将最外面看作一个大的未决策点)。可以用拓扑排序搞定,若有环则无解。
然后我们枚举每个未决策点的四个方向,看一下一直走下去会走到哪个点,在新图中从这个点到终点连一条边。得到新图的出度矩阵和邻接矩阵,求出|出度矩阵-邻接矩阵|即可。
注:内向树:出度矩阵,外向树:入度矩阵。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
#define p(A,B) (((A)-1)*m+(B))
using namespace std;
typedef long long ll;
const ll P=1000000007;
int T,n,m,tot,cnt;
ll ans;
int x[310],y[310],from[40010],to[40010],next[40010],head[40010];
ll v[310][310];
char str[210][210];
queue<int> q;
inline void add(int a,int b)
{
to[cnt]=b,next[cnt]=head[a],head[a]=cnt++;
}
void work()
{
scanf("%d%d",&n,&m);
int i,j,k,a,b,u;
memset(from,-1,sizeof(from)),memset(head,-1,sizeof(head)),cnt=tot=0;
q.push(0),from[0]=0;
for(i=1;i<=n;i++)
{
scanf("%s",str[i]+1);
for(j=1;j<=m;j++)
{
if(str[i][j]=='.') x[++tot]=i,y[tot]=j,q.push(p(i,j)),from[p(i,j)]=tot;
else
{
a=i,b=j;
if(str[i][j]=='L') b--;
if(str[i][j]=='R') b++;
if(str[i][j]=='U') a--;
if(str[i][j]=='D') a++;
if(!a||!b||a>n||b>m) add(0,p(i,j));
else add(p(a,b),p(i,j));
}
}
}
while(!q.empty())
{
u=q.front(),q.pop();
for(i=head[u];i!=-1;i=next[i]) from[to[i]]=from[u],q.push(to[i]);
}
for(i=1;i<=n*m;i++) if(from[i]==-1)
{
puts("0");
return ;
}
memset(v,0,sizeof(v));
for(i=1;i<=tot;i++)
{
a=x[i],b=y[i];
if(a<n) v[i][from[p(a+1,b)]]--;
if(a>1) v[i][from[p(a-1,b)]]--;
if(b<m) v[i][from[p(a,b+1)]]--;
if(b>1) v[i][from[p(a,b-1)]]--;
v[i][i]+=4;
}
for(i=1;i<=tot;i++) for(j=1;j<=tot;j++) if(v[i][j]<0) v[i][j]+=P;
for(ans=1,i=1;i<=tot;i++)
{
for(j=i;j<=tot;j++) if(v[j][i]) break;
if(j!=i) for(ans=P-ans,k=i;k<=tot;k++) swap(v[i][k],v[j][k]);
for(j=i+1;j<=tot;j++)
{
ll A=v[i][i],B=v[j][i],tmp,temp;
while(B)
{
tmp=A/B,temp=A,A=B,B=temp%B;
for(ans=P-ans,k=i;k<=tot;k++) v[i][k]=(v[i][k]-tmp*v[j][k]%P+P)%P,swap(v[i][k],v[j][k]);
}
}
ans=ans*v[i][i]%P;
}
printf("%lld\n",ans);
}
int main()
{
//freopen("C.in","r",stdin);
scanf("%d",&T);
while(T--) work();
return 0;
}