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\n");
        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\n", ans % mod);

    return 0;
}

 

posted @ 2017-08-10 21:32  zbtrs  阅读(290)  评论(0编辑  收藏  举报