• Solution -「JSOI2008」「洛谷 P4208」最小生成树计数


    (mathcal{Description})

      link.
      给定带权简单无向图,求其最小生成树个数。
      顶点数 (nle10^2),边数 (mle10^3),相同边权的边数不超过 (10)

    (mathcal{Solution})

      先说一个引理:对于一个图的任意两棵最小生成树,其边权集合相等。
      简单证明一下,设有两个最小生成树的边权集合 ({dots,a,b,dots},{dots,c,d,cdots})(省略号处相等,不降排列)。相当于第一棵最小棵树的 (a,b) 边替换为了 (c,d) 边形成第二棵。不妨设 (c<ale b<d)。那么在第一棵树里先删去 (a,b) 边,此时图由三个联通块。加入 (c),显然 (a,b) 中的一条是能够再加入的。所以加入 (d) 不优,第二棵不是最小生成树,矛盾。
      借此,先跑出一棵最小生成树,记为 (T),并得到每种边权的出现次数。枚举每种边权 (w),把(T) 中且边权不为 (w) 的边加入图,并加入边权为 (w) 的所有边。注意加入边权为 (w) 的边前需要缩点以保证不会漏选其余边。矩阵树求出此时生成树个数,最后乘法原理乘起来就得到答案了。复杂度 (mathcal O(n^3))

    (mathcal{Code})

    #include <cstdio>
    #include <algorithm>
    
    #define fr first
    #define sc second
    
    const int MOD = 31011, MAXN = 100, MAXM = 1000;
    int n, m, fa[MAXN + 5], col[MAXN + 5], K[MAXN + 5][MAXN + 5];
    bool used[MAXM + 5];
    std::pair<int, std::pair<int, int> > eset[MAXM + 5];
    
    inline void init ( const int n ) { for ( int i = 1; i <= n; ++ i ) fa[i] = i; }
    
    inline int find ( const int x ) { return x ^ fa[x] ? fa[x] = find ( fa[x] ) : x; }
    
    inline bool unite ( const int a, const int b ) {
    	int u = find ( a ), v = find ( b );
    	return u ^ v ? fa[u] = v, true : false;
    }
    
    inline void add ( const int u, const int v ) {
    	++ K[u][u], ++ K[v][v], -- K[u][v], -- K[v][u];
    	if ( K[u][v] < 0 ) K[u][v] += MOD;
    	if ( K[v][u] < 0 ) K[v][u] += MOD;
    }
    
    inline int det ( const int n ) {
    	int ret = 1, swp = 1;
    	for ( int i = 1; i < n; ++ i ) {
    		for ( int j = i + 1; j < n; ++ j ) {
    			for ( ; K[j][i]; std::swap ( K[i], K[j] ), swp *= -1 ) {
    				int d = K[i][i] / K[j][i];
    				for ( int k = i; k < n; ++ k ) K[i][k] = ( K[i][k] - d * K[j][k] + MOD ) % MOD;
    			}
    		}
    		if ( ! ( ret = ret * K[i][i] % MOD ) ) return 0;
    	}
    	return ( ret * swp + MOD ) % MOD;
    }
    
    int main () {
    	scanf ( "%d %d", &n, &m );
    	for ( int i = 1, u, v, w; i <= m; ++ i ) {
    		scanf ( "%d %d %d", &u, &v, &w );
    		eset[i] = { w, { u, v } };
    	}
    	sort ( eset + 1, eset + m + 1 ), init ( n );
    	int cnt = 0;
    	for ( int i = 1; i <= m && cnt < n - 1; ++ i ) {
    		if ( unite ( eset[i].sc.fr, eset[i].sc.sc ) ) {
    			++ cnt, used[i] = true;
    		}
    	}
    	if ( cnt < n - 1 ) return puts ( "0" ), 0;
    	int ans = 1;
    	for ( int i = 1, j; i <= m; i = j + 1 ) {
    		init ( n );
    		for ( j = 1; j <= m; ++ j ) {
    			if ( used[j] && eset[i].fr ^ eset[j].fr ) {
    				unite ( eset[j].sc.fr, eset[j].sc.sc );
    			}
    		}
    		int blk = 0;
    		for ( j = 1; j <= n; ++ j ) if ( j == fa[j] ) col[j] = ++ blk;
    		for ( j = 1; j <= n; ++ j ) col[j] = col[find ( j )];
    		for ( j = 1; j <= blk; ++ j ) for ( int k = 1; k <= blk; ++ k ) K[j][k] = 0;
    		for ( j = i; j <= m; ++ j ) {
    			add ( col[eset[j].sc.fr], col[eset[j].sc.sc] );
    			if ( j == m || eset[j].fr ^ eset[j + 1].fr ) break;
    		}
    		ans = ans * det ( blk ) % MOD;
    	}
    	printf ( "%d
    ", ans );
    	return 0;
    }
    
  • 相关阅读:
    SQLSERVER排查CPU占用高的情况
    SQLSERVER排查CPU占用高的情况
    查看SQL SERVER数据库的连接数
    查看SQL SERVER数据库的连接数
    SQL Server 运行状况监控SQL语句
    SQL Server 运行状况监控SQL语句
    Java8高阶函数及判断高阶函数的方式
    Java8高阶函数及判断高阶函数的方式
    mybatis中association和collection的column传入多个参数问题
    mybatis中association和collection的column传入多个参数问题
  • 原文地址:https://www.cnblogs.com/rainybunny/p/13224232.html
Copyright © 2020-2023  润新知