1. 程式人生 > 實用技巧 >題解 [NOI2014]魔法森林

題解 [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);
}