• 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'为答案生成树的最大边,则[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;
    		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
    ", GetAns());
    	return 0;
    }
    

      

  • 相关阅读:
    cpuset
    top
    path-lookup
    strace
    IDR算法[原理]
    cgroup
    转载
    std::reverse_iterator::base
    可重入、不可重入
    chromium code 中 普遍使用的 C++11 语法
  • 原文地址:https://www.cnblogs.com/headboy2002/p/9532335.html
Copyright © 2020-2023  润新知