【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 ",ans); } int main() { //freopen("C.in","r",stdin); scanf("%d",&T); while(T--) work(); return 0; }