参考题目:SPOJ HIGH
对了有一道假题目
解析:
先明确几个定义。
对于一个图 ,定义其度数矩阵为 ,为一个 的矩阵,满足当 的时候, ,而大对角线上的元素 值为节点 的度数。
对于一个图 ,定义其邻接矩阵为 ,为一个 的矩阵,满足元素 等于 之间直接连接的边数,也称为接数。
对于一个图 ,定义其 矩阵( )如下,为一个 的矩阵,满足对于 。
定理内容描述如下。
对于一个图 ,其生成树个数等于 。其中 表示该矩阵行列式的值。
证明是个很妙妙的东西。
然而太长了不想写
那就主要讲一讲怎么 求一个矩阵的行列式吧。
首先行列式的定义要补充一下。
一个 方阵 的行列式记为 或 。
把一个元素 所在行列划去后(不是置为0,而是整行整列消去),剩余的矩阵的行列式称为元素 的余子式,记作 ,而 称作 的代数余子式,而行列式的值定义如下
好的上面这个完全不说人话的表述方式你看懂了吗?
接下来是一个稍微说人话一点的表述方式。
以一个
的方阵为例。
就是这个矩阵中 绿色的数之积+蓝色数之积+橙色数之积+红色数之积,
上面的结果减去这个矩阵中 绿色的数之积+蓝色数之积+橙色数之积+红色数之积,
上面的差的绝对值就是矩阵行列式的值。
其他大小的矩阵行列式以此类推。
于是行列式就有一些奇奇怪怪的性质。
比如一行(列),加上或减去另一行(列)每个元素的相同倍数,行列式的值不变。(证明可能什么时候才会补吧)
那么我们就可以利用高斯消元将当前矩阵消成一个上三角矩阵,那么就可以轻易求出行列式的值了。
其行列式的值就变成了对角线上的所有值之积。
证明很简单,因为这时候其他地方的积的式子中都含有至少一个元素为0,那么有贡献的就只剩下这条大对角线了。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const
inline
int getint(){
re int num=0;
re char c;
while(!isdigit(c=gc()));
while(isdigit(c))num=(num<<1)+(num<<3)+(c^48),c=gc();
return num;
}
double a[13][13];
inline
double gauss(int n){
for(int re i=1;i<=n;++i){
int pos=i;
for(int re j=i+1;j<=n;++j){
if(fabs(a[j][i])>fabs(a[pos][i]))pos=j;
}
if(fabs(a[pos][i])<=1e-6)return 0;
if(pos!=i)swap(a[pos],a[i]);
for(int re j=i+1;j<=n;++j){
double tmp=a[j][i]/a[i][i];
for(int re k=1;k<=n;++k){
a[j][k]-=a[i][k]*tmp;
}
}
}
double ret=1;
for(int re i=1;i<=n;++i){
ret*=a[i][i];
}
return fabs(ret);
}
signed main(){
int T=getint();
while(T--){
int n=getint(),m=getint();
memset(a,0,sizeof a);
while(m--){
int u=getint(),v=getint();
a[u][u]+=1,a[v][v]+=1;
a[u][v]-=1,a[v][u]-=1;
}
printf("%.0lf\n",gauss(n-1));
}
return 0;
}