bzoj 4031 小z的房间

矩阵树定理是用于判断一个连通图的生成树个数,主要步骤如下:
1.求出一个矩阵等于度数矩阵-邻接矩阵
2.去掉最后一行、最后一列
3.把这个矩阵高斯消元变成倒三角,记录交换次数
4.把a[i][i]乘起来,再乘上(-1)^交换次数,就是生成树个数。
我懒得去看证明了。
这道题丧心病狂的模1e9,不能求逆元,用欧几里德原理来算答案。
5x+……=0
3x+……=0
=>
3x+……=0
2x+……=0
……
很神奇,不是吗?
然后我写错了,这道题是求的图的生成树个数,也就是计算矩阵的时候那些柱子不算图中的点,要不没有边相连,生成树个数肯定是0

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>

#define md 1000000000LL
#define ll long long
#define inf (int) 1e9
#define eps 1e-8
#define N 110
using namespace std;
int n,m;
int g[11][11]; bool mp[11][11];
char st[11];
ll a[N][N];
int tot;
int lx[4]={1,0,-1,0},ly[4]={0,-1,0,1};
void ycl()
{
tot=0;
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
if (mp[i][j]) g[i][j]=++tot;
}
void solve(int n)
{
for (int i=1;i<=n;i++)
{
for (int j=1;j<=n;j++)
if (a[i][j]<0) a[i][j]+=md;
}
int f=1;
for (int i=1;i<=n;i++)
{
for (int j=i+1;j<=n;j++)
{
ll A=a[i][i],B=a[j][i];
while (B)
{
ll t=A/B; A%=B; swap(A,B);
for (int k=i;k<=n;k++) { a[i][k]=(a[i][k]+md-t*a[j][k]%md)%md; swap(a[i][k],a[j][k]);}
f=-f;
}
}
}
ll ans=1; for (int i=1;i<=n;i++) ans=ans*a[i][i]%md;
if (f==-1) ans=(md-ans)%md; printf("%lld\n",ans);
}


int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
{
scanf("%s",st+1);
for (int j=1;j<=m;j++) mp[i][j]=st[j]=='.';
}
ycl();
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
{
if (!mp[i][j]) continue;
for (int k=0;k<=3;k++)
{
int x=i+lx[k],y=j+ly[k];
if (x<1||y<1||x>n||y>m||(!mp[x][y])) continue;
int p=g[i][j],q=g[x][y];
a[p][p]++; a[p][q]--;
}
}
solve(tot-1);
return 0;
}


你可能感兴趣的:(bzoj 4031 小z的房间)