【BZOJ 3669】【NOI 2014】魔法森林 LCT+枚举边

$LCT+枚举$ 复习一下$LCT$模板。

先以$Ai$为关键字$sort$,然后$Ai$从小到大枚举每条边,看能否构成环,构不成则加边,构成则判断,判断过了就切断$Bi$最大的边。

我的边是编号为$i+n$的点,忘了这点调了好久$QAQ$ $sosad$

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 150003
#define read(x) x=getint()
using namespace std;
inline int getint() {int k = 0, fh = 1; char c = getchar();	for(; c < ‘0‘ || c > ‘9‘; c = getchar()) if (c == ‘-‘) fh = -1; for(; c >= ‘0‘ && c <= ‘9‘; c = getchar()) k = k * 10 + c - ‘0‘; return k * fh;}
struct nodeE {int a, b, x, y;} E[N];
struct node *null;
struct node {
	node *ch[2], *fa;
	int d, pos;
	short rev;
	bool pl() {return fa->ch[1] == this;}
	bool check() {return fa == null || (fa->ch[0] != this && fa->ch[1] != this);}
	void push() {if (rev) {rev = 0; swap(ch[0], ch[1]); ch[0]->rev ^= 1; ch[1]->rev ^= 1;}}
	void count() {
		pos = d;
		if (E[ch[0]->pos].b > E[pos].b) pos = ch[0]->pos;
		if (E[ch[1]->pos].b > E[pos].b) pos = ch[1]->pos;
	}
	void setc(node *r, bool c) {ch[c] = r; r->fa = this;}
} *rt[N];
node pool[N];
int n, m, tot = 0;
namespace LCT {
	int ans = 0x7fffffff;
	bool cmp(nodeE X, nodeE Y) {return X.a < Y.a;}
	node *newnode(int num = 0) {
		node *t = &pool[++tot];
		t->ch[0] = t->ch[1] = t->fa = null;
		t->d = t->pos = num; t->rev = 0;
		return t;
	}
	void Build() {
		null = &pool[0];
		null->ch[0] = null->ch[1] = null->fa = null;
		null->d = null->pos = null->rev = 0;
		read(n); read(m);
		for(int i = 1; i <= m; ++i)
			{read(E[i].x); read(E[i].y); read(E[i].a); read(E[i].b);}
		sort(E + 1, E + m + 1, cmp);
		for(int i = 1; i <= n; ++i)
			rt[i] = newnode();
		for(int i = 1; i <= m; ++i)
			rt[n + i] = newnode(i);
		E[0].b = 0;
	}
	void rotate(node *r) {
		node *f = r->fa;
		bool c = r->pl();
		if (f->check()) r->fa = f->fa;
		else f->fa->setc(r, f->pl());
		f->setc(r->ch[!c], c);
		r->setc(f, !c);
		f->count();
	}
	void update(node *r) {if (!r->check()) update(r->fa); r->push();}
	void splay(node *r) {
		update(r);
		for(; !r->check(); rotate(r))
			if (!r->fa->check()) rotate(r->pl() == r->fa->pl() ? r->fa : r);
		r->count();
	}
	node *access(node *r) {node *y = null;	for(; r != null; y = r, r = r->fa) {splay(r); r->ch[1] = y;} return y;}
	void changert(node *r) {access(r)->rev ^= 1; splay(r);}
	void link(node *r, node *t) {changert(r); r->fa = t;}
	void cut(node *r, node *t) {changert(r); access(t); splay(t); t->ch[0]->fa = null; t->ch[0] = null;}
	node *findrt(node *r) {access(r); splay(r); while(r->ch[0] != null) r = r->ch[0]; return r;}
	int ask(node *r, node *t) {changert(r); access(t); splay(t); return t->pos;}
	void work(int u, int v, int edge) {
		if (findrt(rt[u]) == findrt(rt[v])) {
			int k = ask(rt[u], rt[v]);
			if (E[k].b > E[edge - n].b) {
				cut(rt[u], rt[k + n]);
				cut(rt[v], rt[k + n]);
				link(rt[u], rt[edge]);
				link(rt[v], rt[edge]);
			}
		} else {
			link(rt[u], rt[edge]);
			link(rt[v], rt[edge]);
		}
		if (findrt(rt[1]) == findrt(rt[n]))
			ans = min(ans, E[edge - n].a + E[ask(rt[1], rt[n])].b);
	}
	void AC() {printf("%d\n", ans == 0x7fffffff ? -1 : ans);}
}

int main() {
	LCT::Build();
	for(int i = 1; i <= m ;++i)
		LCT::work(E[i].x, E[i].y, i + n);
	LCT::AC();
	return 0;
}

我的代码就是一堵墙,让$300$行的$LinkCutTree$在压行大法前颤抖吧~~~

时间: 2024-08-09 10:35:26

【BZOJ 3669】【NOI 2014】魔法森林 LCT+枚举边的相关文章

[BZOJ 3669][NOI 2014]魔法森林(Link-Cut Tree)

题目链接:http://www.lydsy.com:808/JudgeOnline/problem.php?id=3669 记得四个月之前的NOI同步赛,我还只会玩脚丫子.... 记得我当时看到这个题整个人就吓傻了,完全不知道怎么做,然后NOI同步赛就这样爆零了... 如今我学了LCT这个神器,再看这个题,感觉不再那么难了. 其实这个题的标准解法是SPFA,改得完全认不出来的SPFA. orz太神了,完全没见识过这么神犇的SPFA,NOIP 2014考了SPFA,NOI 2014也考了SPFA,

【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

【bzoj 3669】[Noi2014]魔法森林

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

NOI 2014 魔法森林(BZOJ 3669) 题解

对边按a权值排序,按b权值建LCT,按排序后的顺序依次加边.如果加边后形成环则删除环上最大的边.如果起点终点联通则更新答案. 1 #include<cstdio> 2 #include<algorithm> 3 #define rep(i,n) for(int i=0;i<n;++i) 4 const int MAXN=50000+5; 5 const int MAXM=100000+5; 6 const int INF=~0U>>1; 7 struct Node

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

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

【BZOJ3669】[Noi2014]魔法森林 LCT

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

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]魔法森林——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! 代码中 //// 的地方是我还有点不

Vijos1865 NOI2014 魔法森林 LCT维护生成树

基本思路: 首先按照weightA升序排序,然后依次在图中加边,并维护起点到终点路径上weightB的最大值 如果加边过程中生成了环,则删除环中weightB最大的边 由于是无向图,点之间没有拓扑序,所以在建立LCT模型时,可以将原图的边也视为点,这样就转化成了维护路径上的点权最大值(Orz Hzwer) 点的连通性可以用并查集维护 AC code:(其实Splay双旋一次时只需要进行3次update,而代码中舍弃了这个优化) 1 #include <cstdio> 2 #include &l