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

题目 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;
}
相关推荐
Object~1 分钟前
【第七课】Rust所有权系统(三)
算法
xxxxxmy19 分钟前
移动零
java·算法·力扣
sweetheart7-728 分钟前
LeetCode33. 搜索旋转排序数组(2024冬季每日一题 8)
c++·算法·二分查找·力扣·折半查找
项目申报小狂人1 小时前
ESC算法/逃生:一种基于人群疏散行为的优化方法
算法
九圣残炎1 小时前
【从零开始的LeetCode-算法】3239. 最少翻转次数使二进制矩阵回文 I
java·算法·leetcode
环境感知1 小时前
基于MATLAB的激光雷达与相机联合标定原理及实现方法——以标定板为例
开发语言·人工智能·数码相机·算法·matlab
SuhyOvO3 小时前
std::sort的底层原理(混合排序算法)
c++·算法·stl·排序算法
licy__3 小时前
python常用的排序算法
python·算法·排序算法
行码棋3 小时前
【机器学习】决策树算法原理详解
算法·决策树·机器学习