图论之最小生成树计数(最小生成树的应用)

题目 2401:

信息学奥赛一本通T1492-最小生成树计数

时间限制: 2s 内存限制: 192MB 提交: 18 解决: 8

题目描述

原题来自:JSOI 2008

现在给出了一个简单无向加权图。你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树。(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的)。

输入格式

第一行包含两个数,n 和 m,表示该无向图的节点数和边数,每个节点用 1∼n 的整数编号;

接下来的 m 行,每行包含两个整数:a,b,c,表示节点 a,b 之间的边的权值为 c。

输出格式

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

样例输入

复制

复制代码
4 6
1 2 1
1 3 1
1 4 1
2 3 2
2 4 1
3 4 1

样例输出

复制

复制代码
8

思路:

首先,明确两条性质。

1.所有最小生成树的不同边权的数量一致。如一个图对应的所有最小生成树边权为1,2,3这样的边数固定不变,不会变成3条2,可以想象如果要换掉一条边,那么就必须那一条相等权的边换,大了就不是最小了,小了前一个就不是最小了。

2.相同边权的边所形成的联通块相同,可以想象kruskal建树过程,由小边到大边,小边够了之后联通块不变,等到有大边了,连通块才接着改变。

那么解题思路就变成了统计不同权边的使用方案数,再相乘即答案。

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
const int N=110,M=1010,mod=31011;
int n,m;
int p[N];
struct edge{
    int a,b,w;
    bool operator<(edge&W){
        return w<W.w;
    }
}e[M];
struct tree{
    int l,r,num,sum;
}t[M];
int idx;
int cnt;
int k;
int find(int x){
    if(x!=p[x]){
        return find(p[x]);
    }
    return x;
}
void dfs(int i,int q,int nu){
    if(nu==t[i].num){
        k++;
        return;
    }
    if(q>t[i].r){
        return;
    }
    int a=e[q].a;
    int b=e[q].b;
    int x=find(a);
    int y=find(b);
    if(x!=y){
        p[x]=y;
        dfs(i,q+1,nu+1);
        p[x]=x;
    }
    dfs(i,q+1,nu);
    return;
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        int a,b,c;
        cin>>a>>b>>c;
        e[i].a=a,e[i].b=b,e[i].w=c;
    }
    sort(e+1,e+1+m);
    for(int i=1;i<=n;i++){
        p[i]=i;
    }
    for(int i=1;i<=m;i++){
        int a=e[i].a;
        int b=e[i].b;
        int c=e[i].w;
        if(c!=e[i-1].w){
            idx++;
            t[idx].l=i;
            t[idx-1].r=i-1;
        }
        t[idx].sum++;
        int x=find(a);
        int y=find(b);
        if(x!=y){
            p[x]=y;
            t[idx].num++;
            cnt++;
        }
    }
    t[idx].r=m;
    if(cnt<n-1){
        cout<<0;
        return 0;
    }
    for(int i=1;i<=n;i++){
        p[i]=i;
    }
    int ans=1;
    for(int i=1;i<=idx;i++){
        //cout<<t[i].l<<endl;
        k=0;
        dfs(i,t[i].l,0);
        //cout<<k<<endl;
        if(k){
           ans=(long long )(ans*k)%mod;
        }
        for(int j=t[i].l;j<=t[i].r;j++){
            int a=e[j].a;
            int b=e[j].b;
            int x=find(a);
            int y=find(b);
            if(x!=y){
                p[x]=y;
            }
        }
    }
    cout<<ans;
}
相关推荐
吃着火锅x唱着歌1 小时前
LeetCode 1128.等价多米诺骨牌对的数量
算法·leetcode·职场和发展
十八岁讨厌编程2 小时前
【算法训练营 · 补充】LeetCode Hot100(中)
算法·leetcode
橘颂TA2 小时前
【剑斩OFFER】算法的暴力美学——最小覆盖字串
算法·c/c++·就业
wearegogog1232 小时前
基于混合蛙跳算法和漏桶算法的无线传感器网络拥塞控制与分簇新方法
网络·算法
Tiandaren3 小时前
大模型应用03 || 函数调用 Function Calling || 概念、思想、流程
人工智能·算法·microsoft·数据分析
2301_795167203 小时前
玩转Rust高级应用 如何进行理解Refutability(可反驳性): 模式是否会匹配失效
开发语言·算法·rust
小当家.1054 小时前
[LeetCode]Hot100系列.贪心总结+思想总结
算法·leetcode·职场和发展
墨雪不会编程4 小时前
数据结构—排序算法篇二
数据结构·算法·排序算法
ShineWinsu4 小时前
对于数据结构:堆的超详细保姆级解析—上
数据结构·c++·算法·计算机·二叉树·顺序表·
im_AMBER5 小时前
Leetcode 46
c语言·c++·笔记·学习·算法·leetcode