• [题解] bzoj 1016 jsoi 2008 最小生成树计数 (kruskal最小生成树)


    - 传送门 -

     http://www.lydsy.com/JudgeOnline/problem.php?id=1016

    # 1016: [JSOI2008]最小生成树计数

    Time Limit: 1 Sec  Memory Limit: 162 MB
    Submit: 6066  Solved: 2472
    [Submit][Status][Discuss]

    Description

      现在给出了一个简单无向加权图。你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的
    最小生成树。(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的)。由于不同的最小生
    成树可能很多,所以你只需要输出方案数对31011的模就可以了。

    Input

      第一行包含两个数,n和m,其中1<=n<=100; 1<=m<=1000; 表示该无向图的节点数和边数。每个节点用1~n的整
    数编号。接下来的m行,每行包含两个整数:a, b, c,表示节点a, b之间的边的权值为c,其中1<=c<=1,000,000,0
    00。数据保证不会出现自回边和重边。注意:具有相同权值的边不会超过10条。

    Output

      输出不同的最小生成树有多少个。你只需要输出数量对31011的模就可以了。

    Sample Input

    4 6
    1 2 1
    1 3 1
    1 4 1
    2 3 2
    2 4 1
    3 4 1

    Sample Output

    8
     

    - 题意 -

     不同形态的最小生成树的总个数.
     

    - 思路 -

     先证明: 对于每一种权值的边, 最小生成树用到的数量是一定的.
      先把每个点看成一个集合.
      对于最小权值的边集, 我们先将其全部加入, 消去环, 最后一定会剩下固定数量的集合, 合并的集合也都是一样的.
      处理第二小的边集, 还是先加入, 消去环, 集合的合并也不会受删环上哪一条边的影响(联通的节点/集合是一样的).
      依此类推.
     
     确定了每种边使用数量后, 我们暴搜得到方案数.
     注意暴搜过程中并查集不能路径压缩, 因为有回溯(会删边).
     
     最后乘法原理得到答案.
     
     细节见代码.
     

    - 代码 -

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    
    const int N = 105;
    const int M = 1005;
    const int MOD = 31011;
    
    struct edge {
    	int x, y, v;
    }W[M];
    
    int F[N], L[M], R[M], S[M];
    int n, m, sz, tot, ans = 1, cnt, sum;
    
    bool cmp(edge a, edge b) { return a.v < b.v; }
    
    int find(int x) { return x == F[x] ? x : find(F[x]); }
    
    bool join(int a, int b) {
    	int fa = find(a), fb = find(b);
    	if (fa == fb) return false;
    	F[fa] = fb;
    	return true;
    }
    
    int dfs(int x, int l, int s) {
    	if (l > R[x]) {
    		if (s == S[x])
    			return 1;
    		else
    			return 0;
    	}
    	int tmp = 0, fa = find(W[l].x), fb = find(W[l].y);
    	if (fa != fb) {
    		F[fa] = fb;
    		tmp = dfs(x, l + 1, s + 1); //因为这里回溯需要删边, 但只能维护删掉的边的两端节点的F, 如果未删掉的点的F受路径压缩影响而在删掉的这条边上, F 数组就成功爆炸了(所以有删边时不能用路径压缩!!!!)
    		F[fa] = fa; F[fb] = fb;
    	}
    	return dfs(x, l + 1, s) + tmp;
    }
    
    int main() {
    	scanf("%d%d", &n, &m);
    	for (int i = 1; i <= m; ++i)
    		scanf("%d%d%d", &W[i].x, &W[i].y, &W[i].v);
    	sort(W + 1, W + m + 1, cmp);
    	for (int i = 1; i <= n; ++i)
    		F[i] = i;
    	for (int i = 1; i <= m; ++i) {
    		if (W[i].v != W[i-1].v) {
    			L[++ tot] = i;
    			R[tot - 1] = i - 1;
    		}
    		bool tmp = join(W[i].x, W[i].y);
    		if (tmp) { cnt ++; S[tot] ++; }
    	}
    	R[tot] = m;
    	if (cnt != n - 1) {
    		printf("0
    ");
    		return 0;
    	}
    	for (int i = 1; i <= n; ++i)
    		F[i] = i;
    	for (int i = 1; i <= tot; ++i) {
    		int tmp = dfs(i, L[i], 0);
    		ans = ans * tmp % MOD;
    		for (int j = L[i], t; j <= R[i]; ++j)
    			t = join(W[j].x, W[j].y);
    	}
    	printf("%d
    ", ans);
    	return 0;
    }
    
  • 相关阅读:
    sun.misc.Unsafe的理解
    线程同步工具类
    HashMap源码分析
    【设计模式】GoF设计模式学习总结
    基于ARM处理器的反汇编器软件简单设计及实现
    HashMap内存泄露
    基于Spring cloud Ribbon和Eureka实现客户端负载均衡
    ReentrantLock
    数据库自增主键
    排序
  • 原文地址:https://www.cnblogs.com/Anding-16/p/7358558.html
Copyright © 2020-2023  润新知