LibreOJ#6259. 「CodePlus 2017 12 月赛」白金元首与独舞

Posted ONION_CYC

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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;
}
View Code

 

以上是关于LibreOJ#6259. 「CodePlus 2017 12 月赛」白金元首与独舞的主要内容,如果未能解决你的问题,请参考以下文章

LibreOJ#6299. 「CodePlus 2018 3 月赛」白金元首与克劳德斯

「CodePlus 2017 12 月赛」可做题2(矩阵快速幂+exgcd)

BZOJ5110[CodePlus2017]Yazid 的新生舞会 线段树

LibreOJ #517. 「LibreOJ β Round #2」计算几何瞎暴力

LibreOJ #514. 「LibreOJ β Round #2」模拟只会猜题意

LibreOJ #515. 「LibreOJ β Round #2」贪心只能过样例