zoukankan      html  css  js  c++  java
  • 高斯消元/矩阵树定理学习记录

    高斯消元

    模板 luogu P3389

    #include<bits/stdc++.h>
    using namespace std;
    
    const double eps=1e-6;
    const int N=100+5;
    double a[N][N];
    int n;
    
    int gauss(){
    	int c,r;
    	for(c=1,r=1;c<=n;++c){
    		int t=r;
    		for(int i=r;i<=n;++i){
    			if(fabs(a[i][c])>fabs(a[t][c])){
    				t=i;
    			}
    		}//选最大的(提高精度+将0交换到下面)
    		if(fabs(a[t][c])<eps) continue;
    		for(int i=c;i<=n+1;++i) swap(a[t][i],a[r][i]);//交换到上部
    		for(int i=n+1;i>=c;--i) a[r][i]/=a[r][c];//行首行列式数值变成1
    		for(int i=r+1;i<=n;++i){
    			if(fabs(a[i][c])>eps){
    				double d=a[i][c];
    				for(int j=n+1;j>=c;--j){
    					a[i][j]-=a[r][j]*d;
    				}
    			}
    		}
    		++r;
    	}
    	if(r<=n){
            for(int i=r;i<=n;++i){
                if(fabs(a[i][n+1])>eps){
                    return 2;//无解
                }
            }
    		return 0;//没有唯一解
    	}
    	for(int i=n;i>0;--i){
    		for(int j=i+1;j<=n;++j){
    			a[i][n+1]-=a[j][n+1]*a[i][j];
    		}
    	}
    	return 1;
    }
    
    int main(){
    	scanf("%d",&n);
    	for(int i=1;i<=n;++i){
    		for(int j=1;j<=n+1;++j){
    			scanf("%lf",&a[i][j]);
    		}
    	}
    	if(gauss()!=1){
    		for(int i=1;i<=n;++i){
    			printf("%.2lf
    ",a[i][n+1]);
    		}
    	}else{
    		puts("No Solution");
    	}
    	return 0;
    } 
    

    ICPC2020 Jinan

    #include<bits/stdc++.h>
    using namespace std;
    
    const int N=200+5;
    int a[N][N],b[N][N];
    int m[N][N];
    typedef long long ll;
    const ll mod=998244353;
    
    ll qpow(ll a,ll p){
    	if(p==0){
    		return 1;
    	}else if(p==1){
    		return a%mod;
    	}
    	ll ret=qpow(a,p>>1);
    	ret=(ret*ret)%mod;
    	if(p&1) ret=(ret*a)%mod;
    	return ret;
    }
    
    int main(){
    	int n;
    	scanf("%d",&n);
    	for(int i=1;i<=n;++i){
    		for(int j=1;j<=n;++j){
    			scanf("%d",&a[i][j]);
    		}
    	}
    	for(int i=1;i<=n;++i){
    		for(int j=1;j<=n;++j){
    			scanf("%d",&b[i][j]);
    		}
    	}
    	int ans=0;
    	for(int k=1;k<=n;++k){
    		memset(m,0,sizeof m);
    		for(int i=1;i<=n;++i){
    			for(int j=1;j<=n;++j){
    				m[i][j]^=a[i][j];
    			}
    			if(b[i][k]){
    				m[i][i]^=1;
    			}
    		}
    		int c,r;
    		for(c=1,r=1;c<=n;++c){
    			int t=r;
    			for(int i=r;i<=n;++i){
    				if(m[i][c]){
    					t=i;
    					break;
    				}
    			}
    			if(m[t][c]==0) continue;
    			if(t!=r){
    				for(int i=c;i<=n;++i){
    					swap(m[t][i],m[r][i]);
    				}
    			}
    			for(int i=r+1;i<=n;++i){
    				if(m[i][c]!=0){
    					int d=m[i][c];
    					for(int j=n;j>=c;--j){
    						m[i][j]=(m[i][j]-m[r][j]*d+4)%2;
    					}
    				}
    			}
    			++r;
    		}
    		ans+=n-r+1;
    	}
    	printf("%lld",qpow(2,ans));
    	return 0;
    }
    

    (mod 2)等价于异或,所以可以用bitset优化一个64的常数。

    矩阵树定理

    本章节的全部内容在 CC BY-SA 4.0SATA 协议之条款下提供,转自OIWIKI
    Kirchhoff 矩阵树定理(简称矩阵树定理)解决了一张图的生成树个数计数问题。

    本篇记号声明

    本篇中的图,无论无向还是有向,都允许重边,但是不允许自环。

    无向图情况

    (G) 是一个有 (n) 个顶点的无向图。定义度数矩阵 (D(G)) 为:

    [D_{ii}(G) = mathrm{deg}(i), D_{ij} = 0, i eq j ]

    (#e(i,j)) 为点 (i) 与点 (j) 相连的边数,并定义邻接矩阵 (A) 为:

    [A_{ij}(G)=A_{ji}(G)=#e(i,j), i eq j ]

    定义 Laplace 矩阵(亦称 Kirchhoff 矩阵)(L) 为:

    [L(G) = D(G) - A(G) ]

    记图 (G) 的所有生成树个数为 (t(G))

    有向图情况

    (G) 是一个有 (n) 个顶点的有向图。定义出度矩阵 (D^{out}(G)) 为:

    [D^{out}_{ii}(G) = mathrm{deg^{out}}(i), D^{out}_{ij} = 0, i eq j ]

    类似地定义入度矩阵 (D^{in}(G))

    (#e(i,j)) 为点 (i) 指向点 (j) 的有向边数,并定义邻接矩阵 (A) 为:

    [A_{ij}(G)=#e(i,j), i eq j ]

    定义出度 Laplace 矩阵 (L^{out}) 为:

    [L^{out}(G) = D^{out}(G) - A(G) ]

    定义入度 Laplace 矩阵 (L^{in}) 为:

    [L^{in}(G) = D^{in}(G) - A(G) ]

    记图 (G) 的以 (r) 为根的所有根向树形图个数为 (t^{root}(G,r))。所谓根向树形图,是说这张图的基图是一棵树,所有的边全部指向父亲。

    记图 (G) 的以 (r) 为根的所有叶向树形图个数为 (t^{leaf}(G,r))。所谓叶向树形图,是说这张图的基图是一棵树,所有的边全部指向儿子。

    定理叙述

    矩阵树定理具有多种形式。其中用得较多的是定理 1、定理 3 与定理 4。

    定理 1(矩阵树定理,无向图行列式形式) 对于任意的 (i),都有

    [t(G) = det L(G)inom{1,2,cdots,i-1,i+1,cdots,n}{1,2,cdots,i-1,i+1,cdots,n} ]

    其中记号 (L(G)inom{1,2,cdots,i-1,i+1,cdots,n}{1,2,cdots,i-1,i+1,cdots,n}) 表示矩阵 (L(G)) 的第 (1,cdots,i-1,i+1,cdots,n) 行与第 (1,cdots,i-1,i+1,cdots,n) 列构成的子矩阵(原矩阵去掉第(i)行同时去掉第(i)列((1leq ileq n)),即矩阵的主子式)。也就是说,无向图的 Laplace 矩阵具有这样的性质,它的所有 (n-1) 阶主子式都相等。

    定理 2(矩阵树定理,无向图特征值形式)(lambda_1, lambda_2, cdots, lambda_{n-1})(L(G))(n - 1) 个非零特征值,那么有

    (t(G) = frac{1}{n}lambda_1lambda_2cdotslambda_{n-1})

    定理 3(矩阵树定理,有向图根向形式) 对于任意的 (k),都有

    [t^{root}(G,k) = det L^{out}(G)inom{1,2,cdots,k-1,k+1,cdots,n}{1,2,cdots,k-1,k+1,cdots,n} ]

    因此如果要统计一张图所有的根向树形图,只要枚举所有的根 (k) 并对 (t^{root}(G,k)) 求和即可。

    定理 4(矩阵树定理,有向图叶向形式) 对于任意的 (k),都有

    [t^{leaf}(G,k) = det L^{in}(G)inom{1,2,cdots,k-1,k+1,cdots,n}{1,2,cdots,k-1,k+1,cdots,n} ]

    因此如果要统计一张图所有的叶向树形图,只要枚举所有的根 (k) 并对 (t^{leaf}(G,k)) 求和即可。

    「HEOI2015」小 Z 的房间

    #include<bits/stdc++.h>
    using namespace std;
    
    const int N=105;
    char mp[N][N];
    int n,m;
    typedef long long ll;
    ll L[N][N];
    int id[N][N],cnt;
    const ll mod=1e9;
    
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;++i){
    		scanf("%s",mp[i]+1);
    	}
    	for(int i=1;i<=n;++i){
    		for(int j=1;j<=m;++j){
    			if(mp[i][j]=='.'){
    				id[i][j]=++cnt;
    				if(mp[i-1][j]=='.'){
    					L[id[i][j]][id[i-1][j]]--;
    					L[id[i-1][j]][id[i][j]]--;
    					L[id[i][j]][id[i][j]]++;
    					L[id[i-1][j]][id[i-1][j]]++;
    					
    				}
    				if(mp[i][j-1]=='.'){
    					L[id[i][j]][id[i][j-1]]--;
    					L[id[i][j-1]][id[i][j]]--;
    					L[id[i][j]][id[i][j]]++;
    					L[id[i][j-1]][id[i][j-1]]++;
    				}
    				
    			}
    		}
    	}
    	int r=cnt-1;
    	for(int i=1;i<=r;++i){
    		for(int j=1;j<=r;++j){
    			L[i][j]=(L[i][j]+mod)%mod;
    		}
    	}
    	ll ans=1;
    	for(int i=1;i<=r;++i){
    		for(int j=i+1;j<=r;++j){
    			while(L[j][i]){
    				ll t=L[i][i]/L[j][i];
    				for(int k=i;k<=r;++k){
    					L[i][k]=(L[i][k]-t*L[j][k]%mod+mod)%mod;
    					swap(L[i][k],L[j][k]);
    				}
    				ans*=-1;
    			}
    		}
    		ans=ans*L[i][i]%mod;
    	}
     	printf("%lld",(ans+mod)%mod);
    	return 0;
    } 
    

    注意处理整数时,写法的不同。

    Luogu P2144 [FJOI2007]轮状病毒

    需要高精度

    n=int(input())
    n=n+1
    a=[[0 for i in range(n+2)]for j in range(n+2)]
    a[1][1]=n-1
    a[2][n]=-1
    a[n][2]=-1
    for i in range(2,n+1):
        a[i][1]=-1
        a[1][i]=-1
        a[i][i]=3
        if i+1<=n :
            a[i][i+1]=-1
            a[i+1][i]=-1
    ans=1
    for i in range(1,n):
        for j in range(i+1,n):
            while a[j][i]!=0:
                t=a[i][i]//a[j][i]
                for k in range(i,n):
                    a[i][k]-=t*a[j][k]
                    a[i][k],a[j][k]=a[j][k],a[i][k]
                ans=-ans
        ans*=a[i][i]
    print(ans)
    
  • 相关阅读:
    深入理解计算机系统 第六章 存储器层次结构 第二遍
    深入理解计算机系统 第六章 存储器层次结构
    深入理解计算机系统 第八章 异常控制流 Part2 第二遍
    深入理解计算机系统 第八章 异常控制流 part2
    深入理解计算机系统 第八章 异常控制流 Part1 第二遍
    深入理解计算机系统 第八章 异常控制流 part1
    深入理解计算机系统 第三章 程序的机器级表示 Part2 第二遍
    深入理解计算机系统 第三章 程序的机器级表示 part2
    深入理解计算机系统 第三章 程序的机器级表示 Part1 第二遍
    深入理解计算机系统 第三章 程序的机器级表示 part1
  • 原文地址:https://www.cnblogs.com/chwhc/p/14581456.html
Copyright © 2011-2022 走看看