luogu4234 最小差值生成树

题目大意

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

题解

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‘的定义矛盾。

  引理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, Id;
		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:
	LCT()
	{
		for (int i = 1; i <= 100; i++)
			_nodes[i].Id = i;
	}

	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;
		else
			return 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

时间: 2024-08-30 14:24:57

luogu4234 最小差值生成树的相关文章

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

题目链接 先将边排序,这样就可以按从小到大的顺序维护生成树,枚举到一条未连通的边就连上,已连通则(用当前更大的)替换掉路径上最小的边,这样一定不会更差. 每次构成树时更新答案.答案就是当前边减去生成树上最小边的权值. LCT上维护最小边的编号.求最小边把树上的边用vis[]标记即可. 不熟啊. (另外暴力可以排序后枚举一个分界点,在它之后求最小生成树,在它之前求最大生成树) #include <cstdio> #include <cctype> #include <algor

洛谷P4234 最小差值生成树【LCT】

题目描述 给定一个标号为从 1 到 n 的.有 m 条边的无向图,求边权最大值与最小值的差值最小的生成树. 输入格式: 第一行两个数 n, m ,表示图的点和边的数量. 第二行起 m 行,每行形如$ u_i, v_i, w_i$ ,代表 $u_i$ 到 $v_i$有一条长为 $w_i$的无向边. 输出格式: 输出一行一个整数,代表你的答案. 数据保证存在至少一棵生成树. 输入样例#1: 4 6 1 2 10 1 3 100 1 4 90 2 3 20 2 4 80 3 4 40 输出样例#1:

POJ 3552 Slim Span (最小差值生成树)

链接:http://poj.org/problem?id=3522 题意:给你N个点,M条边(无向的):让你求一个生成树它具有:最大边权与最小边权的差是最小的. 分析:使用kru算法求生成树:当我们用最小边求取了生成树后,去除最小边,继续再求生成树,只需要每次求完生成树后跟新答案就可以了:复杂度O(M*M),因为边不多,可以实现. 1 #include<cstdio> 2 #include<iostream> 3 #include<queue> 4 #include&l

luoguP4234 最小差值生成树

https://www.luogu.org/problemnew/show/P4234 按照边的权值从小到大排序,依次加入,并删除能够删除的权值最小的一条边,用 set 维护当前所有边的边权,并查集维护联通性,LCT 维护两点间最小值和 link cut 操作即可 #include <bits/stdc++.h> #define mp make_pair using namespace std; typedef unsigned long long ull; typedef long long

luogu 4234 最小差值生成树 LCT

感觉码力严重下降~ #include <bits/stdc++.h> #define N 400006 #define inf 1000000000 #define setIO(s) freopen(s".in","r",stdin) using namespace std; multiset<int>S; multiset<int>::iterator it; struct Edge { int u,v,c; Edge(int

笔试题:排序后取最小差值数

笔试题:要从5个人中选取2个人作为礼仪,其中每个人的身高范围为160-190,要求2个人的身高差值最小(如果差值相同的话,选取其中最高的两人),以升序输出两个人的身高. Smple input:161 189 167 172 188 Sample outPut: 188 189 public class demo06{ public static void main(String[] args){ int[] arr={161,189,167,172,188};//原始值 //将原始值升序排序

POJ 3264 Balanced Lineup(最大最小差值 线段树水题)

Language: Default Balanced Lineup Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 37122   Accepted: 17383 Case Time Limit: 2000MS Description For the daily milking, Farmer John's N cows (1 ≤ N ≤ 50,000) always line up in the same order.

LeetCode-908. 最小差值 I

给定一个整数数组 A,对于每个整数 A[i],我们可以选择任意 x 满足 -K <= x <= K,并将 x 加到 A[i] 中. 在此过程之后,我们得到一些数组 B. 返回 B 的最大值和 B 的最小值之间可能存在的最小差值. 示例 1: 输入:A = [1], K = 0 输出:0 解释:B = [1] 示例 2: 输入:A = [0,10], K = 2 输出:6 解释:B = [2,8] 示例 3: 输入:A = [1,3,6], K = 3 输出:0 解释:B = [3,3,3] 或

【CCF】最小差值

问题描述: 问题描述 给定n个数,请找出其中相差(差的绝对值)最小的两个数,输出它们的差值的绝对值. 输入格式 输入第一行包含一个整数n. 第二行包含n个正整数,相邻整数之间使用一个空格分隔. 输出格式 输出一个整数,表示答案. 样例输入 51 5 4 8 20 样例输出 1 样例说明 相差最小的两个数是5和4,它们之间的差值是1. 样例输入 59 3 6 1 3 样例输出 0 样例说明 有两个相同的数3,它们之间的差值是0. 数据规模和约定 对于所有评测用例,2 ≤ n ≤ 1000,每个给定