【BZOJ3669】【NOI2014】魔法森林 LCT

题目描述

  给你一个\(n\)个点\(m\)条边的图,每条边有两个边权\(a,b\)。请你找出从\(1\)\(n\)一条路径,使得这条路径上边权\(a\)的最大值\(+\)边权\(b\)的最大值最小。

  \(n\leq 50000,m\leq 100000\)

题解

  我们可以考虑求出当边权\(a\leq\)某个数时边权\(b\)的最大值。

  先把边按边权\(a\)从小到大排序,依次加入,用LCT维护当前边权\(b\)的最小生成树。如果这两个点已经联通,就判断这两个点路径上边的边权\(b\)的最大值,如果大于当前这条边的边权\(b\),就把这条边删掉。否则就不加入这条边。

  每加完一条边我们就可以认为从\(1\)\(n\)的边权\(a\)的最大值为当前这条边的边权\(a\)(否则就会在之前更新到),然后查询\(1\)\(n\)的边权\(b\)的最大值,更新答案。

  时间复杂度:\(O(m\log n)\)

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<utility>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
namespace lct
{
	int a[200010][2];
	int f[200010];
	pii v[200010];
	pii s[200010];
	int r[200010];
	int root(int x)
	{
		return !f[x]||(a[f[x]][0]!=x&&a[f[x]][1]!=x);
	}
	void reverse(int x)
	{
		swap(a[x][0],a[x][1]);
		r[x]^=1;
	}
	void push(int x)
	{
		if(r[x])
		{
			if(a[x][0])
				reverse(a[x][0]);
			if(a[x][1])
				reverse(a[x][1]);
			r[x]=0;
		}
	}
	void mt(int x)
	{
		s[x]=max(v[x],max(s[a[x][0]],s[a[x][1]]));
	}
	void rotate(int x)
	{
		if(root(x))
			return;
		int p=f[x];
		int q=f[p];
		int ps=(x==a[p][1]);
		int qs=(p==a[q][1]);
		int ch=a[x][ps^1];
		if(!root(p))
			a[q][qs]=x;
		a[x][ps^1]=p;
		a[p][ps]=ch;
		if(ch)
			f[ch]=p;
		f[p]=x;
		f[x]=q;
		mt(p);
		mt(x);
	}
	void clear(int x)
	{
		if(!root(x))
			clear(f[x]);
		push(x);
	}
	void splay(int x)
	{
		clear(x);
		int p,q;
		while(!root(x))
		{
			p=f[x];
			if(!root(p))
			{
				q=f[p];
				if((p==a[q][1])==(x==a[p][1]))
					rotate(p);
				else
					rotate(x);
			}
			rotate(x);
		}
	}
	void access(int x)
	{
		int y=x,t=0;
		while(x)
		{
			splay(x);
			a[x][1]=t;
			mt(x);
			t=x;
			x=f[x];
		}
		splay(y);
	}
	void change(int x)
	{
		access(x);
		reverse(x);
	}
	int findroot(int x)
	{
		access(x);
		while(a[x][0])
			x=a[x][0];
		splay(x);
		return x;
	}
	pii query(int x,int y)
	{
		change(x);
		access(y);
		return s[y];
	}
	void link(int x,int y)
	{
		change(x);
		f[x]=y;
	}
	void cut(int x,int y)
	{
		change(x);
		access(y);
		f[a[y][0]]=0;
		a[y][0]=0;
		mt(y);
	}
}
struct edge
{
	int x,y;
	int a,b;
};
edge a[100010];
int cmp(edge a,edge b)
{
	return a.a<b.a;
}
int main()
{
//	freopen("bzoj3669.in","r",stdin);
//	freopen("bzoj3669.out","w",stdout);
	int n,m;
	scanf("%d%d",&n,&m);
	int i;
	for(i=1;i<=m;i++)
		scanf("%d%d%d%d",&a[i].x,&a[i].y,&a[i].a,&a[i].b);
	sort(a+1,a+m+1,cmp);
	int ans=0x7fffffff;
	for(i=1;i<=m;i++)
	{
		if(a[i].x==a[i].y)
			continue;
		if(lct::findroot(a[i].x)==lct::findroot(a[i].y))
		{
			pii s=lct::query(a[i].x,a[i].y);
			if(a[i].b>=a[s.second].b)
				continue;
			lct::cut(s.second+n,a[s.second].x);
			lct::cut(s.second+n,a[s.second].y);
		}
		lct::v[i+n]=pii(a[i].b,i);
		lct::link(a[i].x,i+n);
		lct::link(a[i].y,i+n);
		if(lct::findroot(1)==lct::findroot(n))
			ans=min(ans,a[i].a+lct::query(1,n).first);
	}
	if(ans==0x7fffffff)
		ans=-1;
	printf("%d\n",ans);
	return 0;
}
posted @ 2018-03-06 07:31  ywwyww  阅读(182)  评论(0编辑  收藏  举报