zoukankan      html  css  js  c++  java
  • bzoj1016 [JSOI2008]最小生成树计数

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

    Time Limit: 1 Sec  Memory Limit: 162 MB
    Submit: 6032  Solved: 2452
    [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

    分析:这道题还是比较坑的......首先我们要利用一条性质:一个图的所有的最小生成树的同一边权的边的数目是相等的,也就是说:我们如果把最小生成树上的边权排序,那么所有最小生成树的排列都是一样的。这个要怎么证明呢?为了简便起见,我们假设有2个不同的边权x1,x2,x1<x2,如果x1有2个,x2有2个,另一个最小生成树x1有3个,x2有1个,那么这是不可能的,因为第二个最小生成树的边权和已经比第一个的小了,如果第二个最小生成树x1有1个,x2有3个,这也不是最小生成树,以此类推,就能证明这个性质(可能不是完全正确的,但是明白这个结论就好了)。

         知道这个结论就比较好处理了。我们接下来要做的就是枚举每个边权满足要求的边数,这里满足的要求是fa[x] != fa[y],也就是构成最小生成树的要求,因为有这个限制,所以不能用组合数,于是我们用dfs,看到了最后是不是达到了我们一开始求的那个最小生成树的边权的边的数目。也就是说:我们以第一个最小生成树为模板,后面的生成树的边权数与第一个相等的边的数目必须相等,同时检测是不是满足要求就可以了。

         有一个坑点:这个图可能不能成为一棵树......

         还有一个坑点:并查集不能用路径压缩,不然dfs不好还原.

         还有一个要注意的地方:我们枚举完一个边权的边后,要把这些边全部连起来,为什么呢,因为这为以后判断下一个边权的边是否满足条件奠定基础.

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    const int mod = 31011;
    
    int n, m, fa[1100],cnt,tot,l[1100],r[1100],sum[1100],ans = 1;
    
    struct node
    {
        int a, b, c;
    }e[1010];
    
    bool cmp(node a, node b)
    {
        return a.c < b.c;
    }
    
    int find1(int x)
    {
        if (x == fa[x])
            return x;
        return find1(fa[x]);
    }
    
    int dfs(int x,int y,int z)
    {
        int res = 0;
        if (y == r[x] + 1)
        {
            if (z == sum[x])
                return 1;
            return 0;
        }
        int fx = find1(e[y].a), fy = find1(e[y].b);
        if (fx != fy)
        {
            fa[fx] = fy;
            res += dfs(x, y + 1, z + 1);
            res %= mod;
            fa[fx] = fx;
        }
        res += dfs(x, y + 1, z);
        res %= mod;
        return res;
    }
    
    int main()
    {
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n; i++)
            fa[i] = i;
        for (int i = 1; i <= m; i++)
            scanf("%d%d%d", &e[i].a, &e[i].b, &e[i].c);
        sort(e + 1, e + 1 + m, cmp);
        for (int i = 1; i <= m; i++)
        {
            if (i == 1)
                l[++cnt] = 1;
            else
                if (e[i].c != e[i - 1].c)
                {
                r[cnt] = i - 1;
                l[++cnt] = i;
                }
            int fx = find1(e[i].a), fy = find1(e[i].b);
            if (fx != fy)
            {
                fa[fx] = fy;
                tot++;
                sum[cnt]++;
            }
        }
        r[cnt] = m;
        if (tot != n - 1)
        {
            printf("0
    ");
            return 0;
        }
        for (int i = 1; i <= n; i++)
            fa[i] = i;
        for (int i = 1; i <= cnt; i++)
        {
            int tmp = dfs(i,l[i],0);
            ans = (ans * tmp) % mod;
            for (int j = l[i]; j <= r[i]; j++)
              {
                int fx = find1(e[j].a), fy = find1(e[j].b);
                if (fx != fy)
                    fa[fx] = fy;
            }
        }
        printf("%d
    ", ans % mod);
    
        return 0;
    }
  • 相关阅读:
    git或gitee 提交代码到远程仓库
    gitee 创建代码仓库,并提交本地代码
    Logback 实现日志链路追踪
    navicat 查看,设计并导出数据库 ER图
    Jackson 使用 @JsonFormat 注解进行时间格式化
    Redis 缓存常见问题
    jedis 与 redission 实现分布式锁
    Redis 集群模式搭建
    Redis 哨兵模式高可用
    Notepad++ 正则表达式提取信息
  • 原文地址:https://www.cnblogs.com/zbtrs/p/7341547.html
Copyright © 2011-2022 走看看