如果不谈证明,稍微有点线代基础的人都可以在两分钟内学完所有相关内容。。
行列式随便找本线代书看一下基本性质就好了。
学习资源:
https://www.cnblogs.com/candy99/p/6420935.html
http://blog.csdn.net/Marco_L_T/article/details/72888138
首先是行列式对几个性质(基本上都是用数学归纳法证):
1.交换两行(列),行列式取相反数
2.由1.得若存在两行(列)完全相同则行列式为0
3.上(下)三角行列式即主对角线值之积
只有这三条用得上。
然后就可以直接上Matrix Tree定理了:一个无向图的邻接矩阵减去度数矩阵得到的矩阵的任意n-1阶子矩阵的行列式的绝对值等于其有标号生成树的数目。
其中邻接矩阵-度数矩阵即为基尔霍夫矩阵(又称拉普拉斯算子)。
加强版:有向图的树形图(从根可以走到任意点)个数,将上面的“度数矩阵”改为“入度矩阵即可”(见第二份链接)
若求以x为根的外向树数量,则求删去第x行与第x列的n-1阶子矩阵的行列式即可。
关于行列式的算法,按定义朴素跑复杂度是阶乘级别的,利用上面的性质,初等行变换使之成为上三角矩阵即可。(实际上就是高斯消元)。注意高消中基准元要选绝对值最大的,以及注意判0退出。
下面是几道裸题:
SPOJ Highways 裸题
1 #include<cmath> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #define rep(i,l,r) for (int i=l; i<=r; i++) 6 using namespace std; 7 8 const int N=15; 9 const double eps=1e-8; 10 11 int T,n,m,u,v; 12 double a[N][N]; 13 14 void Gauss(){ 15 n--; 16 rep(i,1,n){ 17 int r=i; 18 rep(j,i+1,n) if (fabs(a[j][i])>fabs(a[r][i])) r=j; 19 if (fabs(a[r][i]<eps)) { puts("0"); return; } 20 if (r!=i) rep(k,1,n) swap(a[r][k],a[i][k]); 21 rep(j,i+1,n){ 22 double t=a[j][i]/a[i][i]; 23 rep(k,i,n) a[j][k]-=t*a[i][k]; 24 } 25 } 26 double ans=1; 27 rep(i,1,n) ans*=a[i][i]; 28 printf("%.0f ",abs(ans)); 29 } 30 31 int main(){ 32 for (scanf("%d",&T); T--; ){ 33 scanf("%d%d",&n,&m); 34 memset(a,0,sizeof(a)); 35 rep(i,1,m) scanf("%d%d",&u,&v),a[u][u]++,a[v][v]++,a[u][v]--,a[v][u]--; 36 Gauss(); 37 } 38 return 0; 39 }
BZOJ4766:
先手工构造出矩阵然后观察规律求出公式。
https://blog.sengxian.com/solutions/bzoj-4766
1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 typedef long long ll; 5 6 ll n,m,P; 7 ll mod(ll x){ return (x<P) ? x : x-P; } 8 9 ll mul(ll a,ll b){ 10 ll res=0; 11 for (; b; b>>=1,a=mod(a+a)) 12 if (b & 1) res=mod(res+a); 13 return res; 14 } 15 16 ll pow(ll a,ll b){ 17 ll res=1; 18 for (; b; b>>=1,a=mul(a,a)) 19 if (b & 1) res=mul(res,a); 20 return res; 21 } 22 23 int main(){ 24 scanf("%lld%lld%lld",&n,&m,&P); 25 printf("%lld ",mul(pow(n,m-1),pow(m,n-1))); 26 return 0; 27 }
BZOJ4031 裸题
这里又个trick,高斯消元的时候因为模数是个合数不好求逆元,所以用辗转相除的方法做就好了。
就是不停对两行相互进行初等行变换直到其中一行第一个数变为0,这样复杂度是log的,并且保证膜意义下不会出错。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define rep(i,l,r) for (int i=l; i<=r; i++) 5 typedef long long ll; 6 using namespace std; 7 8 const int N=105,md=1000000000; 9 int n,m,a[N][N],id[N][N],tot; 10 char s[N][N]; 11 12 void Gauss(int n){ 13 int s=0; 14 rep(i,1,n){ 15 int r=i; 16 rep(j,i+1,n) if (a[j][i]>a[r][i]) r=j; 17 if (!a[r][i]) { puts("0"); return; } 18 if (i!=r){ 19 s^=1; 20 rep(k,i,n) swap(a[i][k],a[r][k]); 21 } 22 rep(j,i+1,n){ 23 while (a[j][i]){ 24 ll t=a[j][i]/a[i][i]; 25 rep(k,i,n) a[j][k]=(a[j][k]-t*a[i][k]%md+md)%md; 26 if (!a[j][i]) break; 27 s^=1; 28 rep(k,i,n) swap(a[i][k],a[j][k]); 29 } 30 } 31 } 32 ll ans=1; 33 rep(i,1,n) ans=ans*a[i][i]%md; 34 if (s) ans=(md-ans)%md; 35 printf("%lld ",ans); 36 } 37 38 void work(){ 39 rep(i,1,m) rep(j,1,n) if (s[i][j]=='.') id[i][j]=++tot; 40 rep(i,1,m) rep(j,1,n) if (id[i][j]){ 41 int u=id[i][j],v; 42 if (i!=1 && s[i-1][j]=='.') 43 v=id[i-1][j],a[u][u]++,a[v][v]++,a[u][v]--,a[v][u]--; 44 if (j!=1 && s[i][j-1]=='.') 45 v=id[i][j-1],a[u][u]++,a[v][v]++,a[u][v]--,a[v][u]--; 46 } 47 rep(i,1,m*n) rep(j,1,m*n) a[i][j]=(a[i][j]+md)%md; 48 } 49 50 int main(){ 51 freopen("bzoj4031.in","r",stdin); 52 freopen("bzoj4031.out","w",stdout); 53 scanf("%d%d",&m,&n); 54 rep(i,1,m) scanf("%s",s[i]+1); 55 work(); Gauss(tot-1); 56 return 0; 57 }