目录
相关资料:
Matrix-Tree定理和模意义下的矩阵行列式:
Matrix-Tree定理和模意义下的矩阵行列式_determinant的博客-CSDN博客
行列式:
自己的理解:
(1)n阶行列式定义:
其中,“ j1j2···jn ” 为n级全排列。
ps: 逆序数即为该数列中每一项前面比它大的数的个数之和。
更一般的:
(2)行列式相关拓展:
特殊行列式:
练习一下计算行列式吧:
(1)
(2)
(3)行列式性质
- 交换任意两行(列),行列式变号。
- 把某一行(列)乘上一个常数,行列式也乘上该常数。
- 把一行(列)乘上一个常数加到另一行(列)上,行列式不变
- 若矩阵中某一行(列)全为0,则行列式为0。
题目链接:【CQOI2018】社交网络
做法: 对于一个有向图,我们构造如下 A 矩阵:(其中 a[i][j] 表示矩阵第 i 行第 j 列的元素) 对于 i != j, a[i][j] 为 i 到 j 的边数的相反数; a[i][i] 为 i 的出度。
(Matrix_tree定理)
对于无向图,a[i][j] 为 i 与 j 之间边数的相反数,a[i][i] 为度数
对于有向图,a[i][j] 为 i 到 j 的边数的相反数,a[i][i] 为出度
那么这个矩阵删去第 1 行第 1 列之后的行列式为以 1 号点为根的树形图的数量。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int maxn = 300, mod = 10007;
int a[maxn][maxn], n;
int ksm(int x,int k) // 快速幂
{
int ans=1;
while(k)
{
if(k & 1) ans = ans*x%mod;
x = x*x%mod;
k >>= 1;
}
return ans;
}
int hls() // 就是把行列式化成上三角行列式,然后求出主对称轴的乘积,即为行列式的值
{
int now=1, ans=1; // 交换行次数导致乘的数,answer
for(int i = 2; i <= n; ++i) // 此题不计算1行1列
{
int pos = i;
for(; pos <= n; ++pos) if(a[pos][i]) break; // 找到第一行i列不为0,交换(为了消元)
if(pos != i) // 自己不为0就不用换
{
now *= -1;
swap(a[i], a[pos]);
}
if(!a[i][i]) return 0; // i列全都为0就返回0(hls性质4)
ans = ans*a[i][i]%mod; // 更新答案
int inv = ksm(a[i][i],mod-2); // 消元
for(int j = i + 1; j <= n; ++j) // 将下面所有行i列消成0
{
if(!a[j][i]) continue; // 本来就是0就不用消了
int mul = a[j][i]*inv % mod; // 计算由i行消j行需要乘的数
for(int k = 2; k <= n; ++k)
a[j][k] = (a[j][k]-a[i][k]*mul)%mod;
}
}
ans = ans*now;
return (ans+mod)%mod; // 负数情况转正数
}
int main()
{
ios::sync_with_stdio(false); cin.tie(NULL);
int m, v, u;
cin >> n >> m;
for(int i = 1; i <= m; ++i)
{
cin >> v >> u; // u->v
a[v][v]++; a[v][u]--;
}
for(int i = 1; i <= n; ++i)
{
for(int j = 1; j <= n; ++j)
{
if(a[i][j] < 0) a[i][j] += mod; // 防止取模出现问题
}
}
cout << hls();
return 0;
}