YBTOJ&洛谷P2387: 魔法森林(LCT)

解析

LCT从板子到算法的入门题吧
有一些不知道的很实用的技巧

把边按a排序从小到大加入边
那么我们只需要维护当前1-n路径上的b的最小值即可
如果这条边两端点本来不连通,就直接link
否则找到路径上b最大的一条边,断掉,再加入当前边(前提是b最大的边的b大于当前边)

实现上,可以把每条边转化成一个虚点
十分方便

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=2e5+100;
const int mod=1e9+7;
const double eps=1e-9;
inline ll read() {
	ll x(0),f(1);
	char c=getchar();
	while(!isdigit(c)) {
		if(c=='-')f=-1;
		c=getchar();
	}
	while(isdigit(c)) {
		x=(x<<1)+(x<<3)+c-'0';
		c=getchar();
	}
	return x*f;
}

int n,m;

#define ls(o) tr[o][0]
#define rs(o) tr[o][1]
int tr[N][2],f[N],rev[N],val[N],mx[N];
inline bool isroot(int x) {
	return tr[f[x]][0]!=x&&tr[f[x]][1]!=x;
}
inline bool which(int x) {
	return tr[f[x]][1]==x;
}
inline void pushup(int x) {
	if(x){
		mx[x]=x;
		if(ls(x)&&val[mx[ls(x)]]>val[mx[x]]) mx[x]=mx[ls(x)];
		if(rs(x)&&val[mx[rs(x)]]>val[mx[x]]) mx[x]=mx[rs(x)];
	}
	return;
}
inline void tag(int x) {
	if(x) {
		rev[x]^=1;
		swap(tr[x][0],tr[x][1]);
	}
}
inline void pushdown(int x) {
	if(rev[x]){
		rev[x]=0;
		tag(tr[x][0]);
		tag(tr[x][1]);
	}
	return;
}
void debug(int x) {
	if(!x) return;
	pushdown(x);
	debug(tr[x][0]);
	printf("debug: x=%d ls=%d rs=%d\n",x,tr[x][0],tr[x][1]);
	debug(tr[x][1]);
	return;
}
inline void rotate(int x) {
	int fa=f[x],gfa=f[fa];
	int d=which(x),son=tr[x][d^1];
	f[x]=gfa;if(!isroot(fa)) tr[gfa][which(fa)]=x;
	f[fa]=x;tr[x][d^1]=fa;
	if(son){f[son]=fa;}tr[fa][d]=son;
	pushup(fa);pushup(x);
	return;
}
int zhan[N];
inline void splay(int x) {
	int y=x,top=0;
	zhan[++top]=y;
	while(!isroot(y)) zhan[++top]=y=f[y];
	while(top) pushdown(zhan[top--]);
	for(int fa; fa=f[x],!isroot(x); rotate(x)) {
		if(!isroot(fa)) which(fa)==which(x)?rotate(fa):rotate(x);
	}
	return;
}
inline void access(int x) {
	for(int y(0); x; y=x,x=f[x]) {
		splay(x);tr[x][1]=y;pushup(x);
		if(y) f[y]=x;
	}
	return;
}
inline void makeroot(int x) {
	access(x);splay(x);tag(x);
	return;
}
inline int findroot(int x) {
	access(x);splay(x);
	while(pushdown(x),tr[x][0]) x=tr[x][0];
	splay(x);
	return x;
}
inline void link(int x,int y) {
	makeroot(x);
	if(findroot(y)==x) return;
	f[x]=y;pushup(y);
	//printf("link: %d -> %d\n",x,y);
}
inline void cut(int x,int y) {
	makeroot(x);access(y);splay(y);
	if(tr[y][0]!=x||tr[x][1]) return;
	tr[y][0]=f[x]=0;pushup(y);
	return;
}
inline void split(int x,int y){//y is father
	makeroot(x);access(y);splay(y);
	return;
}
struct edge{
	int x,y,a,b;
	bool operator < (const edge oth)const{return a<oth.a;}
}e[N];
int main() {
#ifndef ONLINE_JUDGE
	//freopen("a.in","r",stdin);
	//freopen("a.out","w",stdout);
#endif
	n=read();m=read();
	for(int i=1;i<=m;i++) 
		e[i]=(edge){(int)read(),(int)read(),(int)read(),(int)read()};
	sort(e+1,e+1+m);
	int ans(2e9);
	for(int i=1;i<=m;i++){
		int x=e[i].x,y=e[i].y,w=e[i].b;
		val[i+n]=w;
		//printf("\nid=%d (%d %d) w=%d (a=%d)\n",i+n,x,y,w,e[i].a);
		if(findroot(x)!=findroot(y)) link(x,n+i),link(y,n+i);
		else{
			split(x,y);
			int now=mx[y];
			if(val[now]>w){
				splay(now);
				f[tr[now][0]]=0;tr[now][0]=0;
				f[tr[now][1]]=0;tr[now][1]=0;
				pushup(now);
				link(n+i,x);link(n+i,y);
			}
		}
		if(findroot(1)==findroot(n)){
			split(1,n);
			ans=min(ans,val[mx[n]]+e[i].a);
			//debug(n);
		}
	}
	printf("%d\n",ans<2e9?ans:-1);
	return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值