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)
    
  • 相关阅读:
    java实现第五届蓝桥杯神奇算式
    java实现第五届蓝桥杯扑克序列
    java实现第五届蓝桥杯扑克序列
    java实现第五届蓝桥杯扑克序列
    java实现第五届蓝桥杯扑克序列
    java实现第五届蓝桥杯扑克序列
    java实现第五届蓝桥杯圆周率
    java实现第五届蓝桥杯圆周率
    java实现第五届蓝桥杯圆周率
    java实现第五届蓝桥杯圆周率
  • 原文地址:https://www.cnblogs.com/chwhc/p/14581456.html
Copyright © 2011-2022 走看看