题解 [NOI2014]魔法森林

这题看起来一大堆 LCT 的题解,那我就来发一个动点 SPFA 的吧。

SPFA 是啥不用我说了吧。

动点SPFA

这是一个动态加边的算法。

我们现在假设图中已经有了一些边,从源点\(v0\)的最短路为\(d_1,d_2,d_3,\dots,d_n\)

我们考虑加一条边\((u,v,d)\),表示从\(u\)\(v\)的一条边,长度为\(d\)

这时我们发现我们只要把\(u,v\)插入队列中跑一边 SPFA 就行了。

想一想,为什么。

因为我们只是添加了这一条边,别的边的最短路都处理好了。所以如果我们要更新最短路,一定要经过这条边。这下明白了把?

同时由于之前处理过了。所以时间复杂度也会大大降低。

不过由于总会有毒瘤出题人卡 SPFA 所以有别的算法时最好别用

Solution

这题的题解我们首先看到最大值最小会想到二分,但是因为边没有双单调性,所以这样做不具有正确性。

那么我们可以将所有的边按\(a_i\)排序,枚举\(i\),动态加边,动点 SPFA ,求出从\(1\)开始经过的\(b\)边权最大值的最小值,这个就是三角不等式变个形,具体见代码。

然后当前经过的a边权的最大值就是\(a_i\),不然的话就是没经过这条边,和上一次的答案时一样的。

最后取一下最小值就可以了。

代码

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#include<tr1/unordered_map>
#define re register
#define N 401001
#define MAX 2001
#define inf 1e18
#define eps 1e-10
using namespace std;
typedef long long ll;
typedef double db;
inline void read(re ll &ret)
{
    ret=0;re ll pd=0;re char c=getchar();
    while(!isdigit(c)){pd|=c=='-';c=getchar();}
    while(isdigit(c)){ret=(ret<<1)+(ret<<3)+(c&15);c=getchar();}
    ret=pd?-ret:ret;
    return;
}
ll n,m,a,b,x,y,head[N],ans=inf,tot;
ll d[N];
struct edge
{
	ll from,to,x,y,nxt;
	inline friend bool operator <(re edge a,re edge b)
	{
		return a.x<b.x;
	}
}e[N],f[N];
inline void add(re ll u,re ll v,re ll dx,re ll dy)
{
	e[++tot].from=u;
	e[tot].to=v;
	e[tot].x=dx;
	e[tot].y=dy;
	e[tot].nxt=head[u];
	head[u]=tot;
	return;
}
queue<ll>q;
bool vis[N];
signed main()
{
	read(n);
	read(m);
	for(re int i=1;i<=m;i++)
	{
		read(f[i].from);
		read(f[i].to);
		read(f[i].x);
		read(f[i].y);
	}
	sort(f+1,f+m+1);
	for(re int i=1;i<=n;i++)
		d[i]=inf;
	d[1]=0;
	for(re int i=1;i<=m;i++)
	{
		add(f[i].from,f[i].to,f[i].x,f[i].y);
		add(f[i].to,f[i].from,f[i].x,f[i].y);
		q.push(f[i].from);
		q.push(f[i].to);
		vis[f[i].from]=vis[f[i].to]=true;
		while(!q.empty())
		{
			re ll ver=q.front();
			q.pop();
			vis[ver]=false;
			for(re int j=head[ver];j;j=e[j].nxt)
			{
				re ll to=e[j].to,dis=e[j].y;
				if(d[to]>max(d[ver],dis))
				{
					d[to]=max(d[ver],dis);
					if(!vis[to])
					{
						vis[to]=true;
						q.push(to);
					}
				}
			}
		}
		ans=min(ans,d[n]+f[i].x);
	}
	if(ans==inf)
		puts("-1");
	else
		printf("%lld\n",ans);
	exit(0);
}
posted @ 2020-09-03 10:09  CelticOIer  阅读(75)  评论(1编辑  收藏  举报