2018.10.07【NOI2014】【BZOJ3669】【洛谷P2387】魔法森林(LCT)(最小生成树求瓶颈路)

版权声明:转载请声明出处,谢谢配合。 https://blog.csdn.net/zxyoi_dreamer/article/details/82962392

BZOJ传送门

洛谷传送门


解析:

这道题是经典的 L C T LCT 在线求最小生成树的应用。

思路:

直接做显然很麻烦,就算你知道怎么求瓶颈路也很麻烦,因为这是要求两个最大值的和最小,没有办法直接贪心。

那么我们换一个思路,先确定 a a 最大值,然后再考虑在这个最大值下我们能够获得的尽可能小的路径上最大的 b b

那么我们需要将所有边以 a a 为第一关键字从小到大排序,然后做出在限制 a a 的最大值的情况下,关于 b b 的瓶颈路。也就是说,我们需要在线做最小生成树。

那么这个问题就转化成了 L C T LCT 的经典应用,求在线最小生成树


代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const

inline
int getint(){
	re int num;
	re char c;
	while(!isdigit(c=gc()));num=c^48;
	while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48);
	return num;
}

cs int N=50004,M=100005;
int n,m;

struct edge{
	int u,v,a,b;
	friend bool operator<(cs edge &x,cs edge &y){
		return x.a==y.a?x.b<y.b:x.a<y.a;
	}
}e[M];
inline
int sign(int t){if(t<=n)return -0x3f3f3f3f;else return e[t-n].b;}

typedef struct splay_node *point;
struct splay_node{
	point son[2],fa;
	bool tag;
	int val,pos,ori;
	splay_node(){
		son[0]=son[1]=fa=NULL;
		tag=val=pos=ori=0;
	}
	point &lc(){return son[0];}
	point &rc(){return son[1];}
	
	bool isroot(){return !fa||(fa->son[0]!=this&&fa->son[1]!=this);}
	bool which(){return this==fa->son[1];}
	
	void pushdown(){
		if(tag){
			swap(son[0],son[1]);
			if(son[0])son[0]->tag^=1;
			if(son[1])son[1]->tag^=1;
			tag=0;
		}
	}
	void pushup(){
		pos=ori,val=sign(ori);
		if(son[0]&&son[0]->val>val){
			pos=son[0]->pos;
			val=son[0]->val;
		}
		if(son[1]&&son[1]->val>val){
			pos=son[1]->pos;
			val=son[1]->val;
		}
	}
}ttt[N+M];

struct Link_Cut_Tree{
	point tr[N+M];
	Link_Cut_Tree(int n=150000){for(int re i=0;i<=n;++i)tr[i]=&ttt[i],ttt[i].ori=i;}
	
	void Rotate(point now){
		point Fa=now->fa,FA=Fa->fa;
		bool pos=now->which();
		if(FA&&!Fa->isroot())FA->son[Fa->which()]=now;
		Fa->fa=now;
		now->fa=FA;
		Fa->son[pos]=now->son[!pos];
		if(Fa->son[pos])Fa->son[pos]->fa=Fa;
		now->son[!pos]=Fa;
		Fa->pushup();
		now->pushup();
	}
	
	void Splay(point now){
		static point q[N+M];
		static int qn;
		q[qn=1]=now;
		for(point Fa=now;!Fa->isroot();Fa=Fa->fa)q[++qn]=Fa->fa;
		for(int re i=qn;i;--i)q[i]->pushdown();
		for(point Fa=now->fa;!now->isroot();Rotate(now),Fa=now->fa)
		if(!Fa->isroot())Rotate(now->which()==Fa->which()?Fa:now);
	}
	
	void access(point now){
		for(point son=NULL;now;son=now,now=now->fa)
		Splay(now),now->rc()=son,now->pushup();
	}
	
	void makeroot(point now){
		access(now),Splay(now);
		now->tag^=1;
	}
	
	point findroot(point now){
		access(now),Splay(now);
		while(now->lc())now=now->lc();
		return now;
	}
	
	bool connect(point u,point v){
		makeroot(u);
		return findroot(v)==u;
	}
	bool connect(int u,int v){return connect(tr[u],tr[v]);}
	
	void link(point u,point v){
		makeroot(u);
		u->fa=v;
	}
	void link(int u,int v){link(tr[u],tr[v]);}
	
	void cut(point u,point v){
		makeroot(u),access(v),Splay(v);
		u->fa=v->lc()=NULL;
	}
	void cut(int u,int v){cut(tr[u],tr[v]);}
	
	int query(point u,point v){
		makeroot(u),access(v),Splay(v);
		return v->pos;
	}
	int query(int u,int v){return query(tr[u],tr[v]);}
}LCT;

int ans=0x3f3f3f3f;
bool flag;
int ecnt;
signed main(){
	n=getint(),m=getint();
	for(int re i=1;i<=m;++i){
		e[i].u=getint(),e[i].v=getint();
		e[i].a=getint(),e[i].b=getint();
	}
	sort(e+1,e+m+1);
	for(int re i=1,j;i<=m;i=j+1){
		j=i;
		while(e[j+1].a==e[i].a&&j<m)++j;
		for(int re k=i;k<=j;++k){
			if(!LCT.connect(e[k].u,e[k].v)){
				LCT.link(e[k].u,k+n);
				LCT.link(e[k].v,k+n);
				continue;
			}
			int pos=LCT.query(e[k].u,e[k].v)-n;
			if(pos<=0||e[pos].b<=e[k].b)continue;
			LCT.cut(e[pos].u,pos+n);
			LCT.cut(e[pos].v,pos+n);
			LCT.link(e[k].u,k+n);
			LCT.link(e[k].v,k+n);
		}
		if(!flag){flag=LCT.connect(1,n);}
		if(!flag)continue;
		ans=min(ans,e[i].a+e[LCT.query(1,n)-n].b);
	}
	if(flag)cout<<ans<<endl;
	else cout<<-1<<endl; 
	return 0;
}

猜你喜欢

转载自blog.csdn.net/zxyoi_dreamer/article/details/82962392
今日推荐