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);
        }
    }
    
  • 相关阅读:
    Sql Server 2008卸载后再次安装一直报错
    listbox 报错 Cannot have multiple items selected when the SelectionMode is Single.
    Sql Server 2008修改Sa密码
    学习正则表达式
    Sql Server 查询第30条数据到第40条记录数
    Sql Server 复制表
    Sql 常见面试题
    Sql Server 简单查询 异步服务器更新语句
    jQuery stop()用法以及案例展示
    CSS3打造不断旋转的CD封面
  • 原文地址:https://www.cnblogs.com/zhenglier/p/10100660.html
Copyright © 2011-2022 走看看