NOI 2014 魔法森林 题解

题目传送门

题目大意: 有一张图,每条边有两个属性 a i a_i ai b i b_i bi,对于 1 1 1 n n n 的一条路径,它的花费是路径上所有边的 max ⁡ { a i } + max ⁡ { b i } \max\{a_i\}+\max\{b_i\} max{ai}+max{bi},问花费最小的路径的花费是?

题解

有两个属性的话不好处理,我们将一个属性 a i a_i ai 排序,然后将边一条一条加进来,用 L C T LCT LCT 去维护,假如加进来的边连接的两点已经连通了,那么看一下这两点间路径上 b i b_i bi 最大的那条边,假如我加进来的这条边的 b i b_i bi 比他小,那么就把他删掉,然后把这条边加进来。

每次加边后,此时的 max ⁡ { a i } \max\{a_i\} max{ai} 就等于我加进来的这条边的 a i a_i ai,然后 max ⁡ { b i } \max\{b_i\} max{bi} L C T LCT LCT 已经帮我维护好了。

但是要知道 L C T LCT LCT 这个家伙,是会改变树的形态的,也就是说,边的信息存不了,所以对于每条边我们开一个新的点去存他的信息,然后让这个点去和它连接的两点连接。

判断两点是否连通我用了并查集,比 L C T LCT LCT 要快一丢丢。

代码如下:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 200010

struct node{
	node *zuo,*you,*fa,*maxb;
	int x,lazy,edge;
	node(int c,int e):zuo(NULL),you(NULL),fa(NULL),x(c),maxb(this),lazy(0),edge(e){};
	void update()
	{
		maxb=this;
		if(zuo!=NULL&&zuo->maxb->x>maxb->x)maxb=zuo->maxb;
		if(you!=NULL&&you->maxb->x>maxb->x)maxb=you->maxb;
	}
	bool notroot(){return fa!=NULL&&(fa->zuo==this||fa->you==this);}
	void pushdown()
	{
		if(lazy==1)
		{
			node *tt=zuo;zuo=you;you=tt;
			if(zuo!=NULL)zuo->lazy^=1;
			if(you!=NULL)you->lazy^=1;
			lazy=0;
		}
	}
	void pushr()
	{
		lazy^=1;
		pushdown();
	}
};
node *a[maxn];
int n,m;
struct ro{int x,y,a,b;};
ro road[maxn];
bool cmp(ro x,ro y){return x.a<y.a;}
int f[maxn];
int ans=2e9;
int zhaobaba(int x){return x==f[x]?x:f[x]=zhaobaba(f[x]);}
inline int minn(int x,int y){return x<y?x:y;}
node *zhan[maxn];
int t=0;
void splay(node *x)
{
	node *p=x;
	while(p!=NULL)zhan[++t]=p,p=p->fa;
	while(t>0)zhan[t--]->pushdown();
	while(x->notroot())
	{
		node *fa=x->fa,*gfa=fa->fa;
		if(fa->zuo==x)
		{
			fa->zuo=x->you;
			if(x->you!=NULL)x->you->fa=fa;
			x->you=fa;
		}
		else
		{
			fa->you=x->zuo;
			if(x->zuo!=NULL)x->zuo->fa=fa;
			x->zuo=fa;
		}
		fa->fa=x;x->fa=gfa;
		if(gfa!=NULL)
		{
			if(gfa->zuo==fa)gfa->zuo=x;
			if(gfa->you==fa)gfa->you=x;
		}
		fa->update();x->update();
	}
}
void access(node *x)
{
	for(node *y=NULL;x!=NULL;y=x,x=x->fa)
	splay(x),x->you=y,x->update();
}
void makeroot(node *x)
{
	access(x);splay(x);
	x->pushr();
}
void split(node *x,node *y)
{
	makeroot(x);access(y);
	splay(y);
}
void link(node *x,node *y)
{
	makeroot(x);
	x->fa=y;
}
void del(node *x,node *y)
{
	makeroot(x);access(y);splay(y);
	x->fa=y->zuo=NULL;
}

int main()
{
	scanf("%d %d",&n,&m);
	for(int i=1;i<=m;i++)
	scanf("%d %d %d %d",&road[i].x,&road[i].y,&road[i].a,&road[i].b);
	sort(road+1,road+m+1,cmp);
	for(int i=1;i<=n;i++)
	a[i]=new node(0,-1);
	for(int i=n+1;i<=n+m;i++)
	a[i]=new node(road[i-n].b,i-n);
	for(int i=1;i<=n;i++)
	f[i]=i;
	for(int i=1;i<=m;i++)
	{
		int x=road[i].x,y=road[i].y;
		if(zhaobaba(x)==zhaobaba(y))
		{
			split(a[x],a[y]);
			if(road[i].b<a[y]->maxb->x)
			{
				node *p=a[y]->maxb;
				del(p,a[road[p->edge].x]);del(p,a[road[p->edge].y]);
				link(a[x],a[i+n]);link(a[y],a[i+n]);
			}
		}
		else link(a[x],a[i+n]),link(a[y],a[i+n]),f[zhaobaba(y)]=zhaobaba(x);
		if(zhaobaba(1)==zhaobaba(n))split(a[1],a[n]),ans=minn(ans,road[i].a+a[n]->maxb->x);
	}
	if(ans<2e9)printf("%d",ans);
	else printf("-1");
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值