P3037 [USACO11DEC]简化的农场Simplifying the Farm

[ U S A C O 11 D E C ] 简 化 的 农 场 S i m p l i f y i n g   t h e   F a r m [USACO11DEC]简化的农场Simplifying\ the\ Farm [USACO11DEC]Simplifying the Farm


D e s c r i p t i o n \mathcal{Description} Description
农夫约翰在一所夜校学习算法课程,他刚刚学会了最小生成树。现在约翰意识到他的农场设计得不够高效,他想简化农场的布局。

约翰的农场可以看做一个图,农田代表图中顶点,田间小路代表图中的边,每条边有一定的长度。约翰注意到,农场中最多有三条小路有着相同的长度。约翰想删除一些小路使得农场成为一棵树,使得两块农田间只有一条路径。但是约翰想把农场设计成最小生成树,也就是农场道路的总长度最短。

请帮助约翰找出最小生成树的总长度,同时请计算出总共有多少种最小生成树?


S o l u t i o n \mathcal{Solution} Solution

最 初 想 法 最初想法


正 解 部 分 正解部分
按照 K r u s k a l Kruskal Kruskal 构造最小生成树,
由于题目中给出 " 最 多 有 三 条 小 路 有 着 相 同 的 长 度 " "最多有三条小路有着相同的长度" "" 的条件,
所以遇到 不同 的边有 相同 的边权时, 列举出所有加边时的情况进行分析 ↓ ↓
第一张图为 2 2 2 条边边权相同的情况, 第二张图为 3 3 3 条.

上 图 中   I . 情 况 有 2 种 选 择 , 其 余 皆 无 选 择 余 地 . 上图中 \ I.情况有2种选择,其余皆无选择余地.  I.2,.

上 图 中 只 有 I I I , V I , I V , V I I , 三 个 情 况 有 选 择 余 地 , I I I 有   2   种 选 择 , V I 有   3   中 选 择 , I V 有 3 种 选 择 , V I I 有 2 种 选 择 . 上图中只有III, VI , IV, VII, 三个情况有选择余地, \\ III有\ 2\ 种选择, \\ VI有\ 3\ 中选择, \\ IV有3种选择,\\ VII有2种选择. III,VI,IV,VII,,III 2 ,VI 3 ,IV3,VII2.


实 现 部 分 实现部分
K r u s k a l Kruskal Kruskal 最小生成树算法加新的边时,
使用 s e t set set 辅助判断 碳碳双键,碳碳三键 等价的边,

s e t set set 维护参考了上方的题解.


C o d e \mathcal{Code} Code

#include<bits/stdc++.h>
#define reg register

const int maxn = 100005;
const int mod = 1e9 + 7;

int N;
int M;
int F[maxn];

int Find(int x){ return F[x]==x?x:F[x]=Find(F[x]); }

struct Edge{ int u, v, w; } E[maxn];
bool cmp(Edge a, Edge b){ return a.w < b.w; }

int main(){
        freopen("a.in", "r", stdin);
        freopen("a.out", "w", stdout);
        scanf("%d%d", &N, &M);
        for(reg int i = 1; i <= M; i ++) scanf("%d%d%d", &E[i].u, &E[i].v, &E[i].w);
        for(reg int i = 1; i <= N; i ++) F[i] = i;
        std::sort(E+1, E+M+1, cmp);
        int Ans_1 = 0, Ans_2 = 1;
        for(reg int i = 1; i <= M; i ++){
                typedef std::pair <int, int> pr;
                std::set <pr> S;
                int t, x = 0, y = 0;
                for(t = i; t <= M; t ++){
                        if(E[t].w != E[i].w) break ;
                        int a = E[t].u, b = E[t].v;
                        S.insert(pr(std::min(a, b), std::max(a, b)));
                }
                x = t - i; t --;
                for(; i <= t; i ++){
                        int a = Find(E[i].u), b = Find(E[i].v);
                        if(a != b) F[b] = a, y ++, Ans_1 = (1ll*Ans_1 + E[i].w) % mod;
                }
                i --;
                if(x == 2 && y == 1) Ans_2 = 2ll*Ans_2 % mod;
                else if(x == 3){
                        if(y == 1) Ans_2 = 3ll*Ans_2 % mod;
                        else if(y == 2) Ans_2 = 2ll*Ans_2 % mod;
                }
        }
        printf("%d %d\n", Ans_1, Ans_2);
        return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值