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

题目 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;
}
相关推荐
小孟Java攻城狮4 小时前
leetcode-不同路径问题
算法·leetcode·职场和发展
查理零世4 小时前
算法竞赛之差分进阶——等差数列差分 python
python·算法·差分
小猿_006 小时前
C语言程序设计十大排序—插入排序
c语言·算法·排序算法
熊文豪9 小时前
深入解析人工智能中的协同过滤算法及其在推荐系统中的应用与优化
人工智能·算法
siy233311 小时前
[c语言日寄]结构体的使用及其拓展
c语言·开发语言·笔记·学习·算法
吴秋霖11 小时前
最新百应abogus纯算还原流程分析
算法·abogus
灶龙12 小时前
浅谈 PID 控制算法
c++·算法
菜还不练就废了12 小时前
蓝桥杯算法日常|c\c++常用竞赛函数总结备用
c++·算法·蓝桥杯
金色旭光12 小时前
目标检测高频评价指标的计算过程
算法·yolo
he1010112 小时前
1/20赛后总结
算法·深度优先·启发式算法·广度优先·宽度优先