算法竞赛进阶指南 走廊泼水节

原题链接

题目描述

给定一棵N个节点的树,要求增加若干条边,把这棵树扩充为完全图,并满足图的唯一最小生成树仍然是这棵树。

求增加的边的权值总和最小是多少。

输入格式

第一行包含整数t,表示共有t组测试数据。

对于每组测试数据,第一行包含整数N。

接下来N-1行,每行三个整数X,Y,Z,表示X节点与Y节点之间存在一条边,长度为Z。

输出格式

每组数据输出一个整数,表示权值总和最小值。

每个结果占一行。

数据范围

\(N \le 6000,Z \le 100\)

输入样例:

2
3
1 2 2
1 3 3
4
1 2 3
2 3 4
3 4 5 

输出样例:

4
17 

解题报告

题意理解

这道题目说的很清楚,就是让我们将一个最小生成树的图,添加一些边,使得这张图成为一个完全图.

但是我们这张图的最小生成树,必须还是原来那张图的最小生成树.

也就是说两张图的最小生成树表示是一模一样的.


算法解析

根据上面的信息,我们不难发现这道题目和最小生成树算法联系紧密,那么现在我们的主要问题就在于如何去构造最小生成树.

我们可以考虑最小生成树算法中的Kruskal算法.

  1. 首先将所有的边按照从小到大的顺序排序.

此时我们保证了是最小生成树的完美生成法则.

  1. 对于每一条边\((x,y,w)\)而言,他们之间有某种关系.

假如说\(x\)和\(y\)不在同一个连通块(集合)之中,也就是他们之间没有边相连

那么我们相连之后,现在这两个点,各自所在的连通块(集合),都拥有了一个最短边,也就是\((x,y,w)\).



最小生成树是已经确定了,但是对于这原来两个连通块的其他点怎么办?
\[
首先我们设S_x表示为x之前所在的连通块 \那么S_y表示为y之前所在的连通块.
\]
因为我们不能破坏这个最小生成树,所以我们这原来的两个连通块中的点就必须有如下性质.
\[
假如说点A属于S_x这个集合之中 \点B属于S_y这个集合之中.
\]
那么点\(A\)与点\(B\)之间的距离,必须要大于之前的\(w\),否则就会破坏之前的最小生成树
\[
所以说(A,B)之间的距离最小为w+1
\]



假如说我们知道
\[
S_x有p个元素,然后S_y有q个元素.
\]
那么将
\[
S_x与S_y连通块的所有点相连.
\]
显然这个两个连通块会增加.
\[
p \times q-1条边
\]
然后每一条边的最小长度为
\[
w+1
\]
所以我们会得出
\[
(w+1) \times (p*q-1)为两个连通块成为完全图的最小代价
\]


代码解析

#include <bits/stdc++.h>
using namespace std;
const int N=1e4+100;
int fa[N],n,m,i,j,k,t,s[N];
long long ans;
struct node
{
    int x,y,w;
} edge[N];
bool cmp(node a,node b)
{
    return a.w<b.w;//排序
}
int find(int x)
{
    return fa[x]==x?x:fa[x]=find(fa[x]);//并查集
}
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        for(int i=1;i<n;i++)
            scanf("%d%d%d",&edge[i].x,&edge[i].y,&edge[i].w);
        for(int i=1;i<=n;i++)
            fa[i]=i,s[i]=1;
        sort(edge+1,edge+n,cmp);
        ans=0;
        for(int i=1;i<n;i++)
        {
            int x=find(edge[i].x),y=find(edge[i].y),w=edge[i].w;
            if (x==y)//在同一个连通块之间了
                continue;
            ans+=(long long)(s[x]*s[y]-1)*(w+1);//计算最少路径
            fa[x]=y;//合并
            s[y]+=s[x];//计算连通块大小.
        }
        printf("%lld\n",ans);//输出答案
    }
    return 0;
}

原文地址:https://www.cnblogs.com/gzh-red/p/11013114.html

时间: 2024-11-07 12:22:05

算法竞赛进阶指南 走廊泼水节的相关文章

《算法竞赛进阶指南》学习总结 #include&lt;algorithm&gt;

今天下午大致学完了进阶指南中algorithm头文件下的内容,在这里进行一个总结.   reverse翻转   顾名思义,reverse进行的操作就是翻转原来的顺序,理解非常简单,故不赘述. 操作样例: #include<bits/stdc++.h> using namespace std; vector<int>a; int b[233]; int main() { int na,nb; //vector的实现 scanf("%d",&na); for

《算法竞赛进阶指南》学习总结 二分与三分

首先......我是一个很菜很菜的萌新,所以这篇文章写得很详细,有很多我自己的口水话方便我理解,请各位谨慎食用qwq 以前在网上找过很多介绍二分的博客,但都感觉对萌新不太友好,反正我当时连跳石头都没看懂,所以决定自己写一篇!其中有我的想法,也借鉴了书里的很多内容,感谢lyd. 二分答案,顾名思义,就是对我们所需要的答案进行二分,对我们要求的值进行二分.二分的基础用法是在单调序列或者单调函数当中查找,当答案具有单调性,我们就可以采用二分来计算,当然还有三分,在后面我会详细讲到 整数集合上的二分 在

《算法竞赛进阶指南》0.8总结与练习(1)

116. 飞行员兄弟 "飞行员兄弟"这个游戏,需要玩家顺利的打开一个拥有16个把手的冰箱. 已知每个把手可以处于以下两种状态之一:打开或关闭. 只有当所有把手都打开时,冰箱才会打开. 把手可以表示为一个4х4的矩阵,您可以改变任何一个位置[i,j]上把手的状态. 但是,这也会使得第i行和第j列上的所有把手的状态也随着改变. 请你求出打开冰箱所需的切换把手的次数最小值是多少. 输入格式 输入一共包含四行,每行包含四个把手的初始状态. 符号"+"表示把手处于闭合状态,而

算法竞赛进阶指南做题记录

基本算法 递归与递推 费解的开关 Strange Towers of Hanoi Sumdiv Fractal Streets 前缀和与差分 激光炸弹 IncDec Sequence Tallest Cow 二分 Best Cow Fences 排序 Cinema 货舱选址 七夕祭 Running Median 第K大数 Ultra-QuickSort 奇数码问题 原文地址:https://www.cnblogs.com/Maktub-blog/p/11009723.html

bzoj 1787 &amp;&amp; bzoj 1832: [Ahoi2008]Meet 紧急集合(倍增LCA)算法竞赛进阶指南

题目描述 原题连接 Y岛风景美丽宜人,气候温和,物产丰富. Y岛上有N个城市(编号\(1,2,-,N\)),有\(N-1\)条城市间的道路连接着它们. 每一条道路都连接某两个城市. 幸运的是,小可可通过这些道路可以走遍Y岛的所有城市. 神奇的是,乘车经过每条道路所需要的费用都是一样的. 小可可,小卡卡和小YY经常想聚会,每次聚会,他们都会选择一个城市,使得3个人到达这个城市的总费用最小. 由于他们计划中还会有很多次聚会,每次都选择一个地点是很烦人的事情,所以他们决定把这件事情交给你来完成. 他们

《算法竞赛进阶指南》打卡活动 #0x00 基本算法

101. 最高的牛 题目链接:https://www.acwing.com/problem/content/103/ 作为一个银牌水平的主演数据结构的演员来说,这题现在发现非常好想,每个牛分配一个优先度.我搞一个区间线段树,每次update中间一段使得他们的优先度整体下降到比两端中较小的优先度还要小.然后反过来按照优先度分配身高.哈哈!根本不需要什么算法.不过每次都不询问为什么要线段树呢?差分不香吗?当然不香,每次询问当前的优先度啊! const int MAXN = 10000; int re

《算法竞赛进阶指南》刷题记录

总算闲下来一些辣!然后最近发现其实看书是真真很有效但是一直没有落实!所以决定落实一下这段时间把这本书看完题目做完! 然后发现还有挺多题目挺巧妙的于是一堆博客预警,,,可能最近会写很多比较水(但是我还是不会做)的题目的题解 先放个空壳子晚上再来写qwq 原文地址:https://www.cnblogs.com/lqsukida/p/9988445.html

【算法竞赛进阶指南】USACO07Tallest Cow

前缀和,利用左右端点操作代替对区间的操作,从而优化输入,最后进行一次前缀和的操作,求得结果,这道题里面有个很关键的问题,就是需要去重,本来我想用set,但貌似有点鬼畜,算了,利用map去重,还有pair类型(学一下) #include <iostream> #include <algorithm> #include <map> #include <utility> using namespace std; map<pair<int,int>

【算法竞赛进阶指南】POJ1845Sumdiv

本题目是一道数论的综合题目,主要的一个点就是利用二分法来求等比数列的和,其余的点就是唯一分解定理分解质因数,还有就是快速幂 A^B约数和\(=(1+p_{1}+p_{1}^{2}+\cdot\cdot\cdot+p_{1}^{c1})*(1+p_{2}+p_{2}^{2}+\cdot\cdot\cdot+p_{2}^{c2})*\cdot\cdot\cdot*(1+p_{n}+p_{n}^{2}+\cdot\cdot\cdot+p_{n}^{cn})\) 如果c是奇数(一共有偶数项)\(sum(