【LibreOJ】#6259. 「CodePlus 2017 12 月赛」白金元首与独舞
【题目】给定n行m列的矩阵,每个位置有一个指示方向(上下左右)或没有指示方向(任意选择),要求给未定格(没有指示方向的位置)确定方向,使得从任意一个开始走都可以都出矩阵,求方案数。n,m<=200,k<=300(未定格数量)。
【算法】生成树计数(矩阵树定理)
【题解】先对定向格DFS找环判断是否无解。
然后每个点向指示方向连边,未定格向四周连边,外界作为一个点。
将所有有向边反向后,就是求根为外界的树形图的数量,生成树计数问题用矩阵树定理解决。
复杂度T*O((n*m)^3)。
考虑优化,发现重要的只有未定格的方向且k很小,所以将未定格之间的路径直接计算后只保留未定格的图,再用矩阵树定理解决。
复杂度T*O(k^3)。
注意:
1.逆元的log放在外面,否则复杂度会变成O(k^3*log k)。
2.因为要取模,高斯消元前必须全部变成正数,且矩阵每次交换两行都会使答案取反。
3.有重边,邻接矩阵不能赋值为1,有向边度数矩阵只计算入度。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=310,MOD=1e9+7; const int fx[]={0,0,1,-1}; const int fy[]={1,-1,0,0}; struct edge{int v,from;}e[maxn*2]; struct node{int x,y;}b[maxn]; int n,m,vis[maxn][maxn],v[maxn][maxn],tot,t[maxn][maxn],a[maxn][maxn]; char s[maxn]; bool ok; void dfs(node u,int mark){ if(v[u.x][u.y]||u.x<1||u.x>n||u.y<1||u.y>m)return; vis[u.x][u.y]=mark; int X=u.x+fx[t[u.x][u.y]],Y=u.y+fy[t[u.x][u.y]]; if(vis[X][Y]==mark)ok=0; else if(vis[X][Y])return; else dfs((node){X,Y},mark); } void insert(int u,int v){ a[v][v]++;a[u][v]--;// } void gcd(int a,int b,int& x,int& y){ if(!b){x=1;y=0;} else{gcd(b,a%b,y,x);y-=x*(a/b);} } int inv(int a){ int x,y; gcd(a,MOD,x,y); return (x%MOD+MOD)%MOD; } int 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=1;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;} int 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;// } } } int ans=f?MOD-1:1; for(int i=1;i<=n;i++)ans=1ll*ans*a[i][i]%MOD; return ans;// } int main(){ int T;scanf("%d",&T); while(T--){ memset(v,0,sizeof(v)); memset(vis,0,sizeof(vis)); memset(a,0,sizeof(a)); tot=0; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++){ scanf("%s",s+1); for(int j=1;j<=m;j++){ if(s[j]=='U')t[i][j]=3; if(s[j]=='D')t[i][j]=2; if(s[j]=='L')t[i][j]=1; if(s[j]=='R')t[i][j]=0; if(s[j]=='.'){v[i][j]=++tot;b[tot]=(node){i,j};} } } ok=1; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++)if(!vis[i][j])dfs((node){i,j},(i-1)*m+j); if(!ok)break; } if(!ok){puts("0");continue;} tot++; for(int i=1;i<tot;i++){ int x=b[i].x,y=b[i].y; for(int j=0;j<4;j++){ int X=x+fx[j],Y=y+fy[j];bool yes=0; while(X>=1&&X<=n&&Y>=1&&Y<=m){ if(v[X][Y]){if(v[X][Y]!=i)insert(v[X][Y],i);yes=1;break;} else{ int o1=fx[t[X][Y]],o2=fy[t[X][Y]]; X+=o1;Y+=o2; } } if(!yes)insert(tot,i); } } printf("%d\n",gauss(tot-1)); } return 0; }