[BZOJ 3669] [Noi2014] 魔法森林 【LCT】

题目链接:BZOJ - 3669

题目分析

如果确定了带 x 只精灵A,那么我们就是要找一条 1 到 n 的路径,满足只经过 Ai <= x 的边,而且要使经过的边中最大的 Bi 尽量小。

其实就是一个按照 Bi 建立的 MST 上 1 到 n 的路径。只能使用 Ai <= x 的边。

那么,如果我们从小到大枚举 x ,这样可以使用的边就不断增加,就是在加边的同时维护 MST ,用 LCT 来做就可以了。

如果新加入一条边 (u, v, w) ,并且原 MST 上 u 到 v 的路径中边权最大的边的边权大于 w ,那么就删掉那条边权最大的边,然后把这条新加入的边连到 MST 中。

代码

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>

using namespace std;

inline void Read(int &Num)
{
	char c = getchar();
	while (c < ‘0‘ || c > ‘9‘) c = getchar();
	Num = c - ‘0‘; c = getchar();
	while (c >= ‘0‘ && c <= ‘9‘)
	{
		Num = Num * 10 + c - ‘0‘;
		c = getchar();
	}
}

inline int gmax(int a, int b) {return a > b ? a : b;}
inline int gmin(int a, int b) {return a < b ? a : b;}

const int MaxN = 50000 + 5, MaxM = 100000 + 5, MaxT = 150000 + 5, INF = 999999999;

int n, m, Ans;
int Father[MaxT], Son[MaxT][2], V[MaxT], T[MaxT];

bool isRoot[MaxT], Rev[MaxT];

struct ES
{
	int u, v, p, q;
} E[MaxM];

inline bool Cmp(ES e1, ES e2)
{
	return e1.p < e2.p;
}

/**************************** LCT Start *******************************/

inline int Tmax(int a, int b) {return V[a] > V[b] ? a : b;}

inline void Update(int x)
{
	T[x] = Tmax(x, Tmax(T[Son[x][0]], T[Son[x][1]]));
}

inline void Reverse(int x)
{
	Rev[x] = !Rev[x];
	swap(Son[x][0], Son[x][1]);
}

inline void PushDown(int x)
{
	if (!Rev[x]) return;
	Rev[x] = false;
	if (Son[x][0]) Reverse(Son[x][0]);
	if (Son[x][1]) Reverse(Son[x][1]);
}

inline int GetDir(int x)
{
	if (x == Son[Father[x]][0]) return 0;
	else return 1;
}

void Rotate(int x)
{
	int y = Father[x], f;
	PushDown(y); PushDown(x);
	f = GetDir(x) ^ 1;
	if (isRoot[y])
	{
		isRoot[y] = false;
		isRoot[x] = true;
	}
	else
	{
		if (y == Son[Father[y]][0]) Son[Father[y]][0] = x;
		else Son[Father[y]][1] = x;
	}
	Father[x] = Father[y];
	Son[y][f ^ 1] = Son[x][f];
	if (Son[x][f]) Father[Son[x][f]] = y;
	Son[x][f] = y;
	Father[y] = x;
	Update(y); Update(x);
}

void Splay(int x)
{
	int y;
	while (!isRoot[x])
	{
		y = Father[x];
		if (isRoot[y])
		{
			Rotate(x);
			break;
		}
		if (GetDir(y) == GetDir(x)) Rotate(y);
		else Rotate(x);
		Rotate(x);
	}
}

int Access(int x)
{
	int y = 0;
	while (x != 0)
	{
		Splay(x);
		PushDown(x);
		if (Son[x][1]) isRoot[Son[x][1]] = true;
		Son[x][1] = y;
		if (y) isRoot[y] = false;
		Update(x);
		y = x;
		x = Father[x];
	}
	return y;
}

inline void Make_Root(int x)
{
	int t = Access(x);
	Reverse(t);
}

inline void Link(int x, int y)
{
	Make_Root(x);
	Splay(x);
	Father[x] = y;
}

inline void Cut(int x, int y)
{
	Make_Root(x);
	Access(y);
	Splay(y);
	PushDown(y);
	isRoot[Son[y][0]] = true;
	Father[Son[y][0]] = 0;
	Son[y][0] = 0;
	Update(y);
}

inline int Find_Root(int x)
{
	int t = Access(x);
	while (Son[t][0] != 0) t = Son[t][0];
	return t;
}

/**************************** LCT End *******************************/

int main()
{
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= m; ++i)
	{
		Read(E[i].u); Read(E[i].v);
		Read(E[i].p); Read(E[i].q);
	}
	sort(E + 1, E + m + 1, Cmp); // by ES.p
	for (int i = 1; i <= m; ++i) V[n + i] = E[i].q;
	for (int i = 1; i <= n + m; ++i)
	{
		isRoot[i] = true;
		Father[i] = 0;
		T[i] = i;
	}
	Ans = INF;
	int t, CutE;
	for (int i = 1; i <= m; ++i)
	{
		if (Find_Root(E[i].u) != Find_Root(E[i].v))
		{
			Link(E[i].u, n + i); Link(E[i].v, n + i);
		}
		else
		{
			Make_Root(E[i].u);
			t = Access(E[i].v);
			if (V[T[t]] > E[i].q)
			{
				CutE = T[t];
				Cut(E[i].u, CutE); Cut(E[i].v, CutE);
				Link(E[i].u, n + i); Link(E[i].v, n + i);
			}
		}
		if (Find_Root(1) == Find_Root(n))
		{
			Make_Root(1);
			t = Access(n);
			Ans = gmin(Ans, E[i].p + V[T[t]]);
		}
	}
	if (Ans == INF) Ans = -1;
	printf("%d\n", Ans);
	return 0;
}

  

时间: 2024-10-15 10:04:23

[BZOJ 3669] [Noi2014] 魔法森林 【LCT】的相关文章

BZOJ 3669: [Noi2014]魔法森林( LCT )

排序搞掉一维, 然后就用LCT维护加边MST. O(NlogN) -------------------------------------------------------------------------------------- #include<cstdio> #include<cctype> #include<cstring> #include<algorithm> using namespace std; const int maxn = 1

bzoj 3669: [Noi2014]魔法森林

3669: [Noi2014]魔法森林 Time Limit: 30 Sec  Memory Limit: 512 MB 动点spfa Description 为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士.魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号为1..N,边标号为1..M.初始时小E同学在号节点1,隐士则住在号节点N.小E需要通过这一片魔法森林,才能够拜访到隐士. 魔法森林中居住了一些妖怪.每当有人经过一条边的时候,这条边上的妖怪就会对其发起攻击.幸运的

bzoj 3669: [Noi2014]魔法森林 动态树

3669: [Noi2014]魔法森林 Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 363  Solved: 202[Submit][Status] Description 为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士.魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号为1..N,边标号为1..M.初始时小E同学在号节点1,隐士则住在号节点N.小E需要通过这一片魔法森林,才能够拜访到隐士. 魔法森林中居住了一些妖怪

BZOJ 3669 NOI2014 魔法森林 SPFA

题目大意: 给定一个无向图,每条边有两个权值ai和bi,从1走到N,设路径上a权的最大值为A,b权的最大值为B,求A+B的最小值 首先这题如果只有一个权值就是水题无误--但是多了个权值之后我们就要好好考虑一下了 我们对a排序,枚举a,对于每一次枚举求b权最大值的最小值即可 跑M遍SPFA肯定超时无误 网上很多人写了LInk-Cut-Tree维护动态最小生成树 我的LCT没写明白 就去写了SPFA.... 这里要用的SPFA的动态加点(边)法 我们每加一条边 就把边的两端点入队 继续SPFA 不用

【BZOJ3669】[Noi2014]魔法森林 LCT

[BZOJ3669][Noi2014]魔法森林 Description 为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士.魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号为1..N,边标号为1..M.初始时小E同学在号节点1,隐士则住在号节点N.小E需要通过这一片魔法森林,才能够拜访到隐士. 魔法森林中居住了一些妖怪.每当有人经过一条边的时候,这条边上的妖怪就会对其发起攻击.幸运的是,在号节点住着两种守护精灵:A型守护精灵与B型守护精灵.小E可以借助它们的力量,达到自

【BZOJ】3669: [Noi2014]魔法森林(lct)

http://www.lydsy.com/JudgeOnline/problem.php?id=3669 首先看到题目应该可以得到我们要最小化 min{ max{a(u, v)} + max{b(u, v)} } 两个变量不好做...那么我们约束一个a 即按a从小到大排序,依次加边. 发现当有环出现时,去掉的是环中b最大的边. 证明:因为a是从小到大排序,因此此时答案为 a+max{b(u, v)},显然b越小越好. 然后需要link和cut操作... 脑洞开到这里开不动了...........

【BZOJ 3669】 [Noi2014]魔法森林 LCT维护动态最小生成树

这道题看题意是在求一个二维最小瓶颈路,唯一可行方案就是枚举一维在这一维满足的条件下使另一维最小,那么我们就把第一维排序利用A小的边在A大的情况下仍成立来动态加边维护最小生成树. #include <cstdio> #include <algorithm> namespace Pre{ inline void read(int &sum){ register char ch=getchar(); for(sum=0;ch<'0'||ch>'9';ch=getcha

3669 [Noi2014]魔法森林(LCT,最小生成树)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=3669 [题意] 给定一个无向图,求1-n的路径中最小的max{ai}+max{bi} [思路] 将边按照a排序.LCT维护关于b的最小生成树. 顺序枚举每条边u,v,如果u,v已经连接则比较u,v路径上的最大边与新边,否则直接相连. 如果1与n连通,则用e.a+max{e.b}更新ans.直观地看,最小生成树上的max{e.b}是1..i条边加入后能够得到的最小b. _max的初值赋

bzoj3669 [Noi2014]魔法森林——LCT

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3669 第一道LCT! 主要是看这个博客理解学LCT板子:https://blog.csdn.net/yxuanwkeith/article/details/50991326 关于这道题,又看了看这个博客:https://blog.csdn.net/clove_unique/article/details/51317842 然后努力抄写了半天,成功AC! 代码中 //// 的地方是我还有点不