洛谷4111 [HEOI2015]小Z的房间 (辗转相除+高斯消元)

题目链接

不得不说这个题的出题人是真的毒瘤啊。

首先我们观察这个题。由于要求每个点都达到一次,我们如果对于合法的边全部建出来之后,其实就是求一个生成树个数。

我们可以直接使用矩阵树定理,然后建出来对应的矩阵,然后直接跑高斯消元,求行列式不就完了吗??

仔细一看,woc
模数不是质数,可能不存在逆元!!
QWQ
那应该怎么办呢。

这时候就需要一个黑科技。

辗转相除高斯消元

我们考虑,其实我们高斯消元求行列式的时候,实际上目的就是把当前列的其他行上的元素都变成0,那我们不妨采用辗转相除的方法,,如果 aji = 0,则已经完成。否则,我们需要对 a [ i ] [ i ] a[i][i] a [ j ] [ i ] a[j][i] 进行辗转相除法。
假设一开始 x = a [ i ] [ i ] , y = a [ j ] [ i ] x = a[i][i],y=a[j][i]
t = x / y t = x/y
也就是每次让 x = t y x -= t*y ,然后 s w a p ( x , y ) swap(x,y)
本质就是一个从 ( a , b ) (a,b) 变成 ( b , a m o d    b ) (b,a\mod b) 的过程

由辗转相除法的理论,在有限步后,一定会有一个变成 0 0 。如果是 a [ j ] [ i ] = 0 a[j][i]=0 ,则已经完成任务,否则交换第 i i 行和第 j j 行即可。重复上述操作,直到对所有的 j j a j i = 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;
}

猜你喜欢

转载自blog.csdn.net/y752742355/article/details/85106270
今日推荐