BZOJ3669: [Noi2014]魔法森林【并查集+LCT】

7 篇文章 0 订阅
4 篇文章 0 订阅

3669: [Noi2014]魔法森林

【题目描述】

传送门

【题解】

我们发现只要记录这条路径上的最大值就可以了,那么我们可以强制其中一个为最大值,然后判断是否联通,更新答案(也就是将a排序,然后维护1到n中b的最大值就可以了)

可以用LCT解决,考虑这个两个节点全部被加入,那么我们用这个b替换掉这条路径上最大的b,就可以了。

【代码如下】

#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN=200005;
int n,m,Ans,fa[MAXN];
int get(int x){return fa[x]==x?x:fa[x]=get(fa[x]);}
struct xcw{
	int x,y,a,b;
	bool operator <(const xcw b)const{return a<b.a;}
}a[MAXN];
struct LCT{
	int Son[MAXN][2],Fa[MAXN],Val[MAXN],ID[MAXN];bool rev[MAXN];
	bool IsRoot(int rt){return Son[Fa[rt]][0]!=rt&&Son[Fa[rt]][1]!=rt;}
	bool GetSon(int rt){return Son[Fa[rt]][1]==rt;}
	void Clear(){for(int i=0;i<=n+m;i++){Val[i]=-(1<<30);if(i>n) Val[i]=a[i-n].b;ID[i]=i,Son[i][0]=Son[i][1]=Fa[i]=rev[i]=0;}}
	void PushUp(int rt){
		ID[rt]=rt;
		for(int j=0;j<2;j++)
		if(Val[ID[rt]]<Val[ID[Son[rt][j]]]) ID[rt]=ID[Son[rt][j]];
	}
	void Rotate(int rt){
		int fa=Fa[rt],x=GetSon(rt);
		if(!IsRoot(fa)) Son[Fa[fa]][GetSon(fa)]=rt;
		Son[fa][x]=Son[rt][x^1],Fa[Son[rt][x^1]]=fa;Fa[rt]=Fa[fa];Fa[fa]=rt;Son[rt][x^1]=fa;
		PushUp(fa);PushUp(rt);
	}
	void Flip(int rt){rev[rt]^=1;swap(Son[rt][0],Son[rt][1]);}
	void PushDown(int rt){if(!rev[rt]) return;rev[rt]^=1,Flip(Son[rt][0]),Flip(Son[rt][1]);}
	void DFS(int rt){if(!IsRoot(rt)) DFS(Fa[rt]);PushDown(rt);}
	void Splay(int rt){for(DFS(rt);!IsRoot(rt);Rotate(rt)) if(!IsRoot(Fa[rt])) (GetSon(Fa[rt])==GetSon(rt))?Rotate(Fa[rt]):Rotate(rt);}
	void Access(int rt){for(int lst=0;rt;lst=rt,rt=Fa[rt])Splay(rt),Son[rt][1]=lst,PushUp(rt);}
	void Make_Root(int rt){Access(rt);Splay(rt);Flip(rt);}
	void Link(int x,int y){Make_Root(x),Fa[x]=y;}
	void GetPath(int x,int y){Make_Root(x),Access(y),Splay(y);} 
	void Cut(int x,int y){Make_Root(x),Access(y),Splay(y);Fa[x]=Son[y][0]=0;PushUp(y);}
	int Ask(int x,int y){GetPath(x,y);return Val[ID[y]];}
}T;
#include<cctype>
int read(){
	int ret=0;char ch=getchar();bool f=1;
	for(;!isdigit(ch);ch=getchar()) f^=!(ch^'-');
	for(; isdigit(ch);ch=getchar()) ret=ret*10+ch-48;
	return f?ret:-ret;
}
void Merge(int i){
	int fx=get(a[i].x),fy=get(a[i].y);
	if(fx!=fy) fa[fy]=fx,T.Link(a[i].x,i+n),T.Link(i+n,a[i].y);
	else if(T.Ask(a[i].x,a[i].y)>a[i].b){
		int Ed=T.ID[a[i].y];
		T.Cut(a[Ed-n].x,Ed),T.Cut(Ed,a[Ed-n].y);
		T.Link(a[i].x,i+n),T.Link(i+n,a[i].y);
	}
}
int main(){
	n=read(),m=read();
	for(int i=1;i<=m;i++) a[i]=(xcw){read(),read(),read(),read()};
	sort(a+1,a+1+m);T.Clear();
	for(int i=1;i<=n;i++) fa[i]=i;Ans=1<<30;
	for(int i=1;i<=m;i++){
		Merge(i);if(get(1)!=get(n)) continue;
		Ans=min(Ans,a[i].a+T.Ask(1,n));
	}
	if(Ans==1<<30) printf("-1\n");else printf("%d\n",Ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值