CF76A Gift


题意简述

题目链接

  给定一张无向图和两个权值G、S,图中每条边有两个权值au,ag,求一棵生成树,设树边中最大的权值au为A,最大的权值ag为B,需使下式最小化:G*A+S*B。

算法概述

  【暴力】

  该题要求一棵特殊的最小生成树,显然Kruskal无法直接求出有二维权值限制的最小生成树,所以我们考虑先固定一维。

  然后依次枚举每条边,令A等于当前的au,则au固定,再以权值ag为关键字进行Kruskal,最终必然能够求出答案。Kruskal过程中注意,假设当前枚举的边即为最终的树边中权值au最大者,那么则其余边的权值au必然小于等于之,,则可直接筛去权值au大于之者。

  时间复杂度O(m2logm)

  【正解】

  暴力算法的瓶颈主要在于Kruskal上。我们发现,当前枚举的au为答案的A时,则剩余的树边的权值au必然小于等于之,所以可以想到先以权值au为关键字升序排序,然后开始枚举每条边,固定au,此时我们进行Kruskal时,无需枚举所有的边,由于答案的树边的au必然小于当前边的au,所以直接在排在当前边之前的边中找答案即可。

  那么考虑维护一个栈,以au为关键字枚举时将该边压入栈中,每次压栈时维护栈中以ag为关键字的单调性,便可省去Kruskal的排序操作。然后以此枚举栈中的边,进行Kruskal,将每一轮Kruskal选出的所有边覆盖到栈中,将栈刷新,即可保证栈中最多只有n-1个元素。

  如此,可维持时间复杂度在O(nm)

参考代码

#include 
#include 
#include 
#include 
using namespace std;
typedef long long ll;
const int N=210,M=5e4+10;
const ll INF=9223372036854775807;
struct Edge{
	int u,v,au,ag;
	bool operator <(const Edge &E)const
	{
		if(au!=E.au)return au=2;j--)
			if(stk[j].ag