题目大意

  在一个带权无向图中,它的最小差值生成树为最大边与最小边差值最小的生成树。求一个图的最小差值生成树。

题解

30分解法

  引理1 最小生成树的最大边的边权是所有生成树中最大边边权中的最小值。
  证明:任意一棵生成树都可以在最小生成树的基础上,通过不断取一个树外边e,将其替换掉其与生成树所在环中的一条边的方式而得到。我们就看看第一条用来替换的边的情况吧。在不在最小生成树中的边中任取一个边权小于最小生成树最大边m的边e,则e必然与最小生成树的树边形成环。若m不在环中,那么就是替换掉任意一条边,答案也没有影响。如果m在环中,且用e替换掉m可以得到一个最大边权更小的生成树,那么原来的最小生成树就不是最小生成树了。因此原命题成立。

  因此,我们可以将边排序,不断将最小边删除并求一遍Kruskal,最终取min即可。

100分解法

  拆边,用LCT。先将最小生成树加入LCT中,然后从小到达枚举每一条树外边,将其和树边所在环中最小边删除然后纳入LCT中,每次在外部更新最大值与最小值的差的最小值即可。

解法正确性证明

  证明目标 若答案生成树的最小边权和最大边权为L', R',则当我们按照此方法枚举到R'时,L'就是当前生成树中的最小边权。

  假设在经过R'之前,中间状态生成树的最小边权为L(L < L'),最大边权为R。

  引理2 边权位于[L, R']内的边集中必然存在一条边e,使得e和边权为L的边位于一个环内,且L为最小边权。
  证明:假设命题不成立,如果要使答案为L',边权L的边必须去除。如果[L, R']内没有满足条件的e,则e的边权>R',此时要使R'为答案生成树的最大边,则[L, R']间必然存在一条边e',使得e'与e在一个环内。因为如果边a, b在一个环内,b, c也在一个环内,则a, c也在一个环内,所以e'和边权为L的边在一个环内。这样e'就是一个满足条件的边,与原假设不符。

  引理3 在中间状态下,若边权为L'的边在生成树内,则边权位于[L, R']内的边集中必然不存在一条边e,e和边权为L'的边在一个环内,且L'是环中的最小边权。
  证明:假设命题不成立,L'不在生成树内,答案就不可能是[L', R']。

  引理4 在中间状态下,若边权为L'的边不在生成树内,则在[L, L']中必然存在一条边e,使得边权为L'的边和e在一个环内,且L'不是环中的最小边。
  证明:假设命题不成立,那么在L'所在环中选其它一条边,边权为L'',则L'', R是一个更优的答案,产生了矛盾。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cassert>
using namespace std;const int MAX_NODE = 50010, MAX_EDGE = 200010, INF = 0x3f3f3f3f;
int TotNode, TotEdge, Ans;struct Edge
{int From, To, Weight;bool InTree;bool operator < (const Edge& a) const{return Weight < a.Weight;}
}_edges[MAX_EDGE];
int MaxEdgeP, MinEdgeP;struct LCT
{
private:static const int MAX_TREE_NODE = MAX_NODE + MAX_EDGE;struct Node{int Val;bool Rev;Node *Father, *LeftSon, *RightSon, *MinValP;bool IsRoot(){return !Father || (Father->LeftSon != this && Father->RightSon != this);}bool IsLeftSon(){return Father->LeftSon == this;}void Refresh(){MinValP = this;if (LeftSon && LeftSon->MinValP->Val < MinValP->Val)MinValP = LeftSon->MinValP;if (RightSon && RightSon->MinValP->Val < MinValP->Val)MinValP = RightSon->MinValP;}void Reverse(){swap(LeftSon, RightSon);Rev = !Rev;}void PushDown(){if (Rev){if (LeftSon)LeftSon->Reverse();if (RightSon)RightSon->Reverse();Rev = false;}}}_nodes[MAX_TREE_NODE];struct SplayTree{private:Node *InnerRoot;void PushDown(Node *cur){if (!cur->IsRoot()){PushDown(cur->Father);}cur->PushDown();}void Rotate(Node *cur){Node *gfa = cur->Father->Father;Node **gfaSon = cur->Father->IsRoot() ? &InnerRoot : cur->Father->IsLeftSon() ? &gfa->LeftSon : &gfa->RightSon;Node **faSon = cur->IsLeftSon() ? &cur->Father->LeftSon : &cur->Father->RightSon;Node **curSon = cur->IsLeftSon() ? &cur->RightSon : &cur->LeftSon;*faSon = *curSon;if (*faSon)(*faSon)->Father = cur->Father;*curSon = cur->Father;(*curSon)->Father = cur;*gfaSon = cur;(*gfaSon)->Father = gfa;(*curSon)->Refresh();cur->Refresh();}public:void Splay(Node *cur){PushDown(cur);while (!cur->IsRoot()){if (!cur->Father->IsRoot())Rotate(cur->Father->IsLeftSon() == cur->IsLeftSon() ? cur->Father : cur);Rotate(cur);}}}t;void Access(Node *cur){Node *prev = NULL;while (cur){t.Splay(cur);cur->RightSon = prev;cur->Refresh();prev = cur;cur = cur->Father;}}void MakeRoot(Node *cur){Access(cur);t.Splay(cur);cur->Reverse();}void MakePath(Node *u, Node *v){MakeRoot(v);Access(u);t.Splay(u);}void Link(Node *u, Node *v){MakeRoot(v);v->Father = u;}void Cut(Node *u, Node *v){MakePath(u, v);assert(v->Father == u);assert(u->LeftSon == v);u->LeftSon = NULL;v->Father = NULL;u->Refresh();}Node *FindRoot(Node *cur){while (cur->Father)cur = cur->Father;while (cur->LeftSon)cur = cur->LeftSon;return cur;}Node *GetMinNode(Node *u, Node *v){if (FindRoot(u) != FindRoot(v))return NULL;MakePath(u, v);return u->MinValP;}public:void SetNode(int v, int val){_nodes[v].Val = val;}void Link(int u, int v){Link(_nodes + u, _nodes + v);}void Cut(int u, int v){Cut(_nodes + u, _nodes + v);}int GetMinId(int u, int v){Node *ans = GetMinNode(_nodes + u, _nodes + v);if (ans == NULL)return -1;elsereturn ans - _nodes;}
}g;void InitBuild()
{sort(_edges + 1, _edges + TotEdge + 1);for (int i = 1; i <= TotEdge; i++)g.SetNode(TotNode + i, _edges[i].Weight);for (int i = 1; i <= TotNode; i++)g.SetNode(i, INF);int cnt = 0, curEdge = 0;while (cnt < TotNode - 1){curEdge++;int k = g.GetMinId(_edges[curEdge].From, _edges[curEdge].To);if (k == -1){cnt++;g.Link(_edges[curEdge].To, curEdge + TotNode);g.Link(curEdge + TotNode, _edges[curEdge].From);_edges[curEdge].InTree = true;MaxEdgeP = curEdge;}}Ans = _edges[MaxEdgeP].Weight - _edges[1].Weight;MinEdgeP = 1;
}int GetAns()
{for (int i = 1; i <= TotEdge; i++){if (_edges[i].InTree)continue;_edges[i].InTree = true;if (_edges[i].Weight > _edges[MaxEdgeP].Weight)MaxEdgeP = i;int cutEdge = g.GetMinId(_edges[i].From, _edges[i].To);assert(cutEdge != -1);g.Cut(_edges[cutEdge - TotNode].To, cutEdge);g.Cut(cutEdge, _edges[cutEdge - TotNode].From);_edges[cutEdge - TotNode].InTree = false;if (MaxEdgeP == cutEdge - TotNode)while (!_edges[MaxEdgeP].InTree)MaxEdgeP--;if (MinEdgeP == cutEdge - TotNode)while (!_edges[MinEdgeP].InTree)MinEdgeP++;Ans = min(Ans, _edges[MaxEdgeP].Weight - _edges[MinEdgeP].Weight);g.Link(_edges[i].To, i + TotNode);g.Link(i + TotNode, _edges[i].From);}return Ans;
}int main()
{scanf("%d%d", &TotNode, &TotEdge);for (int i = 1; i <= TotEdge; i++){scanf("%d%d%d", &_edges[i].From, &_edges[i].To, &_edges[i].Weight);while (_edges[i].From == _edges[i].To){TotEdge--;scanf("%d%d%d", &_edges[i].From, &_edges[i].To, &_edges[i].Weight);}}InitBuild();printf("%d\n", GetAns());return 0;
}

  

转载于:https://www.cnblogs.com/headboy2002/p/9532335.html

luogu4234 最小差值生成树相关推荐

  1. 洛谷.4234.最小差值生成树(LCT)

    题目链接 先将边排序,这样就可以按从小到大的顺序维护生成树,枚举到一条未连通的边就连上,已连通则(用当前更大的)替换掉路径上最小的边,这样一定不会更差. 每次构成树时更新答案.答案就是当前边减去生成树 ...

  2. [洛谷P4234]最小差值生成树

    给定一个标号为从$1$到$n$的.有$m$条边的无向图,求边权最大值与最小值的差值最小的生成树. 做法类似魔法森林,首先求出来最小生成树,然后每次加入一条边,断掉环上最小边并更新答案 这个过程我用两个 ...

  3. LeetCode简单题之学生分数的最小差值

    题目 给你一个 下标从 0 开始 的整数数组 nums ,其中 nums[i] 表示第 i 名学生的分数.另给你一个整数 k . 从数组中选出任意 k 名学生的分数,使这 k 个分数间 最高分 和 最 ...

  4. mysql获取两个表中日期字段的最小差值

    一.前言 开发中碰到的需求,获取两个数据表中日期字段的差值,并且取得差值最小的那条数据.本篇文章主要讲述如果通过函数获取mysql的日期差值,实际编写时遇到的问题,并且分析需求,得出最终sql等. 二 ...

  5. 算法--06年华为面试:求两个数组的最小差值(Java实现)

    Q题目 华为06年面试题(要求8分钟完成) 有两个数组a,b,大小都为n,数组元素的值任意,无序: 要求:通过交换a,b中的元素,使数组a元素的和与数组b元素的和之间的差最小. A解法 1.常见错误逻 ...

  6. 算法--微软面试题:求一个整数数组元素间最小差值

    Q题目 有一个整数数组,请求出两两之差绝对值最小的值,记住,只要得出最小值即可,不需要求出是哪两个数. A解法 方案一:最愚笨的办法--暴力穷举 利用数组中所有数据两两相减的对比来求出这个最小差值. ...

  7. 「 每日一练,快乐水题 」1984. 学生分数的最小差值

    ✅力扣原题: 力扣链接:1984. 学生分数的最小差值 ✅题目简述: 给你一个 下标从 0 开始 的整数数组 nums ,其中 nums[i] 表示第 i 名学生的分数.另给你一个整数 k . 从数组 ...

  8. 1984. 学生分数的最小差值

    1984. 学生分数的最小差值 给你一个 下标从 0 开始 的整数数组 nums ,其中 nums[i] 表示第 i 名学生的分数.另给你一个整数 k . 从数组中选出任意 k 名学生的分数,使这 k ...

  9. leetcode910. 最小差值 II(贪心)

    给定一个整数数组 A,对于每个整数 A[i],我们可以选择 x = -K 或是 x = K,并将 x 加到 A[i] 中. 在此过程之后,我们得到一些数组 B. 返回 B 的最大值和 B 的最小值之间 ...

  10. LeetCode 1984. 学生分数的最小差值

    文章目录 1. 题目 2. 解题 1. 题目 给你一个 下标从 0 开始 的整数数组 nums ,其中 nums[i] 表示第 i 名学生的分数.另给你一个整数 k . 从数组中选出任意 k 名学生的 ...

最新文章

  1. android nfc peer to peer 实例,NFC Peer2Peer Mode - Android Beam - ISO 18092
  2. 前端学习(752):全局变量和局部变量
  3. linux 培训6,Linux Syscalls有 6个参数(Linux Syscalls with 6 parameters)
  4. 一键托管,阿里云全链路追踪服务正式商用:成本仅自建1/5或更少
  5. linux 链接脚本,Linux下的lds链接脚本简介(一)
  6. CentOS7安装GNOME可视化界面 和 远程访问
  7. paip.本机CVS环境搭建
  8. 原创:CAD批量去除教育版戳记
  9. mysql数据库技术与应用实训项目_MySQL数据库项目实训
  10. android 办公桌面壁纸,android系统励志的壁纸欣赏
  11. 微型计算机自动化控制专业,自动化专业简介
  12. 独秀日记:童道自然大夫山徒步
  13. 婴儿电动摇篮车摇篮床单芯片蓝牙芯片IC方案
  14. 360度全景拍摄,探索全景世界带你飞跃视野新高度
  15. 测试分析之从用户价值角度设计测试点
  16. c语言间隔符号的作用,C语言教学(二)常见的符号
  17. Python——信号量、条件变量、事件
  18. CSS选择器优先级(特异性)
  19. 机器学习笔记之配分函数(三)对比散度
  20. html5 | 通过js实现对网页文本内容语音朗读 | 教程

热门文章

  1. Vid2Vid多图详解
  2. 【something】简单的平均脸制作
  3. leetcode之动态规划刷题总结1(Java)
  4. 遥感影像几何校正模型(RPC模型)
  5. win10更换自己喜欢的鼠标样式,马里奥、wow、lol都可以哦
  6. go语言能开发什么?go语言开发案例
  7. 自动驾驶 4-5 自行车模型的横向动力学 Lateral Dynamics of Bicycle Model
  8. 公路自行车入门级推荐java_开学季:9款值得买公路车推荐
  9. android su程序分析
  10. 一名“老”引擎程序员的昨天、今天和明天