zoukankan      html  css  js  c++  java
  • Organising the Organisation(uva10766)(生成树计数)

    Input

    Output

    Sample Input

    5 5 2
    3 1
    3 4
    4 5
    1 4
    5 3
    4 1 1
    1 4
    3 0 2
    

    Sample Output

    3
    8
    3
    

    题意:

    有一张图上有(n)个点,两两之间有一条边,现在切断(m)条边,求剩下的图中有多少种不同的生成树。

    题解:

    生成树计数

    做这道题,需要三个预备知识:

    (Kirchhoff)矩阵

    首先先构造两个矩阵

    度数矩阵D:是一个(N×N)的矩阵,其中
    (D[i][j]=0(i≠j))(D[i][i]=i)号点的度数

    邻接矩阵A:是一个(N×N)的矩阵,其中
    (A[i][i]=0,A[i][j]=A[j][i]=i),(j)之间的边数

    然后(Kirchhoff)矩阵(K=D-A)

    举个例子,对于如下的无向图,三个矩阵分别为:(图片来源于网络)

    行列式(det(K))及其求法

    考虑对于原矩阵(K),我们可以得到其行列式的求和式:

    (det(A) = sum_{sigma in S_n} sgn(sigma) prod_{i=1}^n a_{i,sigma(i)})

    其中(S_n)是指全排列的集合,(sigma)就是一个全排列,如果(sigma)的逆序对对数为偶数,则(sgn(sigma)=1)否则(=-1)

    然后我们可以用(n!)左右的算法解决了,但这绝对会TLE啊!!!

    让我们来研究一下行列式的性质(我就列了几条有用的):

    • 性质.1 互换矩阵的两行(列),行列式变号。
    • 性质.2 如果该矩阵为一三角形矩阵。则该矩阵的行列式等于A的对角元素的乘积。
    • 性质.3 如果把矩阵的某一行(列)加上另一行(列)的k倍,则行列式的值不变。
    • 性质.4 如果矩阵有两行(列)成比例(比例系数k),则行列式的值为0。
    • 性质.5 互换矩阵的两行(列),行列式变号。

    结合这几个性质,我们可以很明显的看出可以用高斯消元优化了,复杂度为(O(n^3))

    (Matrix-Tree)定理:

    • 对于一个无向图G,它的生成树个数等于其(Kirchhoff)矩阵任何一个n-1阶主子式的行列式的绝对值。

    好吧其实我没看懂这个定理,但经过一番感性理解,大概就是这个意思:

    • 我们把(Kirchhoff)矩阵的第(i)行和第(i)列去掉,剩下矩阵的行列式的绝对值就是生成树的数量。

    其中(i)(1)~(n)中任意一个数(真的是任意一个就可以了),为了方便这题我们直接设它为n就行了。


    有了这几个预备知识后,这一题就可以做了。

    上代码:

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    ll A[60][60],K[60][60];
    ll tree_sum(int n){
        ll ret=1;
        for(int i=1;i<=n;++i){
            if(!K[i][i]){
                bool b=0;
                for(int j=i+1;j<=n;++j){
                    if(K[j][i]){
                        b=1;
                        for(int k=1;k<n;++k){
                            swap(K[i][k],K[j][k]);
                        }ret=-ret;
                        break;
                    }
                }
                if(!b)return 0;
            }
            for(int j=i+1;j<=n;++j){
                while(K[j][i]){
                    ll t=K[i][i]/K[j][i];
                    for(int k=i;k<=n;++k){
                        K[i][k]-=t*K[j][k];
                        swap(K[i][k],K[j][k]);
                    }
                    ret=-ret;
                }
            }ret*=K[i][i];
        }return ret;
    }
    int main(){
        int n,m,k;
        while(~scanf("%d%d%d",&n,&m,&k)){
            memset(A,0,sizeof A);
            memset(K,0,sizeof K);
            for(int i=1;i<=m;++i){
                int x,y;
                scanf("%d%d",&x,&y);
                A[x][y]=A[y][x]=1;
            }
            for(int i=1;i<=n;++i){
                for(int j=1;j<=n;++j){
                    if(i!=j&&!A[i][j]){
                        K[i][i]++;
                        K[i][j]--;
                    }
                }
            }--n;
            ll ans=tree_sum(n);
            printf("%lld
    ",ans);
        }
    }
    
  • 相关阅读:
    Gulp使用入门操作---压缩JS
    Linux如何查看进程及如何杀死进程
    连不上GitHub,也ping不通,亲测解决方案
    idea一键生成mybatis工具
    idea逆向生成hibernate工程
    idea常用快捷键大全
    mysql日期函数
    数据库事务
    idea激活码
    oracle创建表前校验是否存在
  • 原文地址:https://www.cnblogs.com/zhenglier/p/10100660.html
Copyright © 2011-2022 走看看