不得不说这个题的出题人是真的毒瘤啊。
首先我们观察这个题。由于要求每个点都达到一次,我们如果对于合法的边全部建出来之后,其实就是求一个生成树个数。
我们可以直接使用矩阵树定理,然后建出来对应的矩阵,然后直接跑高斯消元,求行列式不就完了吗??
仔细一看,woc
模数不是质数,可能不存在逆元!!
QWQ
那应该怎么办呢。
这时候就需要一个黑科技。
辗转相除高斯消元
我们考虑,其实我们高斯消元求行列式的时候,实际上目的就是把当前列的其他行上的元素都变成0,那我们不妨采用辗转相除的方法,,如果 aji = 0,则已经完成。否则,我们需要对
和
进行辗转相除法。
假设一开始
设
也就是每次让
,然后
本质就是一个从
变成
的过程
由辗转相除法的理论,在有限步后,一定会有一个变成 。如果是 ,则已经完成任务,否则交换第 行和第 行即可。重复上述操作,直到对所有的 , 。
QWQ可以看一下代码 感觉其实不算太好理解
这里直接把辗转相除看成一种把一个元素变成0的过程即可。
// luogu-judger-enable-o2
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define mk make_pair
#define ll long long
#define int long long
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
const int maxn = 210;
const int mod = 1e9;
int a[maxn][maxn];
int n,m;
int d[maxn];
char s[maxn][maxn];
int num[maxn][maxn];
int cnt;
int dx[5]={0,1,0,-1,0};
int dy[5]={0,0,1,0,-1};
int ans=1;
int gauss()
{
int ff=1;
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
a[i][j]=(a[i][j]%mod+mod)%mod;
for (int i=1;i<=n;i++)
{
for (int j=i+1;j<=n;j++)
{
while (a[j][i])
{
int t = a[i][i]/a[j][i];
for (int k=i;k<=n;k++) a[i][k]=(a[i][k]-t*a[j][k]%mod+mod)%mod;
swap(a[i],a[j]);
ff*=(-1);
}
}
ans=ans*a[i][i]%mod;
}
if (ff==-1) return (mod-ans)%mod;
return ans;
}
signed main()
{
n=read();m=read();
for (int i=1;i<=n;i++)
scanf("%s",s[i]+1);
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
if (s[i][j]!='*') num[i][j]=++cnt;
for (int i=1;i<=n;i++)
{
for (int j=1;j<=m;j++)
{
if (s[i][j]=='*') continue;
for (int k=1;k<=4;k++)
{
int x = i+dx[k];
int y = j+dy[k];
if (x<=0 || y<=0 || x>n || y>m) continue;
if (s[x][y]=='*') continue;
a[num[i][j]][num[x][y]]=-1;
d[num[i][j]]++;
}
}
}
for (int i=1;i<=cnt;i++) a[i][i]=d[i];
n=cnt;
n--;
cout<<gauss();
return 0;
}