【BZOJ3669】[Noi2014]魔法森林【Link-Cut Tree】【最小生成树】

【题目链接】

一开始写了个二分a+最短路b,骗了65分,然后改成二分b+最短路a,骗了70分。。发现二分是不对的之后,给答案取min,骗到了90分。出题人太不负责任了。

正解是枚举a,用LCT维护b的最小生成树。

/* Telekinetic Forest Guard */
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int maxn = 50005, maxm = 100005, maxnode = 200005, inf = 0x3f3f3f3f;

int n, m, fa[maxn];

struct _edge {
	int u, v, a, b;

	bool operator < (const _edge &x) const {
		return a != x.a ? a < x.a : b < x.b;
	}
} mp[maxm];

inline int iread() {
	int f = 1, x = 0; char ch = getchar();
	for(; ch < '0' || ch > '9'; ch = getchar()) f = ch == '-' ? -1 : 1;
	for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
	return f * x;
}

inline int find(int x) {
	return fa[x] == x ? x : fa[x] = find(fa[x]);
}

int son[maxnode][2], pre[maxnode], mx[maxnode], val[maxnode];
bool rev[maxnode];
int sta[maxnode];

inline bool isroot(int x) {
	return son[pre[x]][0] != x && son[pre[x]][1] != x;
}

inline void pushup(int x) {
	mx[x] = x;
	if(val[mx[son[x][0]]] > val[mx[x]]) mx[x] = mx[son[x][0]];
	if(val[mx[son[x][1]]] > val[mx[x]]) mx[x] = mx[son[x][1]];
}

inline void pushdown(int x) {
	if(rev[x]) {
		for(int i = 0; i < 2; i++) {
			rev[son[x][i]] ^= 1;
			swap(son[son[x][i]][0], son[son[x][i]][1]);
		}
		rev[x] = 0;
	}
}

inline void maintain(int x) {
	int top = 0;
	for(sta[++top] = x; !isroot(x); x = pre[x]) sta[++top] = pre[x];
	for(; top; pushdown(sta[top--]));
}

inline void rotate(int x) {
	int y = pre[x], z = pre[y], type = son[y][1] == x;
	pre[son[y][type] = son[x][!type]] = y;
	pre[x] = z;
	if(!isroot(y)) son[z][son[z][1] == y] = x;
	pre[son[x][!type] = y] = x;
	pushup(y); pushup(x);
}

inline void splay(int x) {
	maintain(x);
	while(!isroot(x)) {
		int y = pre[x], z = pre[y];
		if(isroot(y)) rotate(x);
		else if(son[z][1] == y ^ son[y][1] == x) rotate(x), rotate(x);
		else rotate(y), rotate(x);
	}
}

inline void access(int x) {
	for(int y = 0; x; x = pre[y = x]) {
		splay(x);
		son[x][1] = y;
		pushup(x);
	}
}

inline void makeroot(int x) {
	access(x); splay(x); rev[x] ^= 1;
	swap(son[x][0], son[x][1]);
}

inline void link(int x, int y) {
	makeroot(x); pre[x] = y;
}

inline void cut(int x, int y) {
	makeroot(x); access(y); splay(y);
	son[y][0] = pre[x] = 0;
}

inline int query(int x, int y) {
	makeroot(x); access(y); splay(y);
	return mx[y];
}

int main() {
	n = iread(); m = iread();
	for(int i = 1; i <= m; i++) mp[i] = (_edge){iread(), iread(), iread(), iread()};
	for(int i = 1; i <= n; i++) fa[i] = i;

	sort(mp + 1, mp + 1 + m);

	int ans = inf;
	for(int i = 1; i <= m; i++) {
		int u = mp[i].u, v = mp[i].v; val[i + n] = mp[i].b; mx[i + n] = i + n;
		if(find(u) != find(v)) {
			link(u, i + n), link(i + n, v);
			fa[find(u)] = find(v);
		} else {
			int x = query(u, v);
			if(val[x] > mp[i].b) {
				cut(mp[x - n].u, x); cut(x, mp[x - n].v);
				link(u, i + n); link(i + n, v);
			}
		}
		if(find(1) == find(n)) ans = min(ans, mp[i].a + val[query(1, n)]);
	}

	if(ans == inf) ans = -1;
	printf("%d\n", ans);
	return 0;
}
时间: 2024-08-29 08:41:44

【BZOJ3669】[Noi2014]魔法森林【Link-Cut Tree】【最小生成树】的相关文章

[bzoj3669][Noi2014]魔法森林_LCT_并查集

魔法森林 bzoj-3669 Noi-2014 题目大意:说不明白题意系列++……题目链接 注释:略. 想法:如果只有1个参量的话spfa.dij什么的都上来了. 两个参量的话我们考虑,想将所有的边按照a排序. 如果两个点:它们之间有两条路径,有一条比另一条劣. 那么我们完全可以将另一条弄掉. 排序之后维护生成树. LCT的点维护的是实子树中第二参量的最大值. 如果当前边连接的两点之前不连通,直接连上. 如果联通,我们判断新边的第二参量和两点之间splay的最大参量之间的关系. 如果新边的第二参

沉迷Link-Cut tree无法自拔之:[BZOJ3669][Noi2014] 魔法森林

来自蒟蒻 \(Hero \_of \_Someone\) 的 \(LCT\) 学习笔记 $ $ 有一个很好的做法是 \(spfa\) ,但是我们不聊 \(spfa\) , 来聊 \(LCT\) \(LCT\) 做法跟 \(spfa\) 的做法其实有点像, 先将所有的边按 \(a\) 的值从小到大排, 再以 \(b\) 的值为边权来动态的维护最小生成树, 答案即为 当前插入边的 \(a\) 值加上最小生成树中的最大边权 的最小值 $ $ 此外, 用 \(LCT\) 维护 \(MST\) , 就是在

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的初值赋

【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

BZOJ3669: [Noi2014]魔法森林

传送门 高级数据结构学傻系列 正解似乎是最短路xjb搞,但是用LCT瞎搞搞也是很吼啊. 从贪心开始,按照每条边a的大小随意sort一下. 对于每个边,我们check两点的联通性,如果联通的话取b最大的值,如果大于当前边的b的话就就删除最大边,把这条边加进去. 如果不连通的话直接添加即可. LCT滋次这些操作,所以大力LCT即可. //BZOJ 3669 //by Cydiater //2017.2.16 #include <iostream> #include <queue> #i

bzoj3669: [Noi2014]魔法森林 lct

记得去年模拟赛的时候好像YY出二分答案枚举a,b的暴力,过了55欸 然后看正解,为了将两维变成一维,将a排序,模拟Kruskal的加边过程,同时维护1到n的最大值,加入一条边e(u,v,a,b)时有以下两种情况: 1) 若u,v已连通,则找出u->v上最大的b',若b<b',则替换之,同时更新答案,注意e一定经过1->n,因为去掉b'所在边时1,n一定不连通,若加上e后1,n连通,则必经过e,由于a是有序的,所以a是路径上最大的a,用a+MAX_b[1->n]更新答案即可. 2)否

BZOJ3669 NOI2014魔法森林

按a从小到大排序,然后按b建图. 每次只需要找1~n中最大的b加当前的a计算答案即可. 这里还有一个小操作就是化边为点,把一条边的边权看做一个点的点权然后多连两条边. 1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=4e5+10; 4 int f[N],fa[N],ma[N],pos[N],c[N][2],rev[N],s[N],n,m,ans=2e9,w[N]; 5 struct node{ 6 int x,y,a

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! 代码中 //// 的地方是我还有点不

【BZOJ3669】[Noi2014]魔法森林 LCT

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