P11336题解
科技:
Dijkstra,最短路树
思路:
首先,以
设操作的两条边编号为
假如
其中标黄的蓝边是
这种情况的最短路是
首先,由于点
接下来考虑
考虑反证法,假设该路径经过了
证毕。
考虑怎么计算答案。
我们称树上
于是可以离线处理。维护一个可重集,从上到下枚举关键点、关键边。枚举所有非树边
我们只用跑两次最短路,做
可能评个蓝或紫?
代码:
#include <bits/stdc++.h>
#define pb push_back
using namespace std;
typedef long long ll;
const int N = 3e5 + 5;
const ll INF = 1e18;
int n,m,tot = 1,top,head[N],to[N << 1],val[N << 1],nxt[N << 1],vis[N << 1],w[N],p[N],id[N],h[N];
ll ans,dis[2][N];
vector <ll> v1[N],v2[N];
multiset <ll> s;
struct Node{int x,pre;ll d;};
bool operator < (Node x,Node y){return x.d > y.d;}
priority_queue <Node> q;
void add_(int x,int y,int z)
{
to[++tot] = y,val[tot] = z;
nxt[tot] = head[x],head[x] = tot;
}
void dijkstra(int s,int t)
{
memset(dis[t],127,sizeof(dis[t]));
dis[t][s] = 0,q.push((Node){s,0,0});
while(!q.empty())
{
Node tmp = q.top();
q.pop();
int x = tmp.x,pre = tmp.pre;
ll d = tmp.d;
if(d > dis[t][x]) continue;
vis[pre] = t;
for(int i = head[x],y;i;i = nxt[i])
{
y = to[i];
if(d + val[i] < dis[t][y])
{
dis[t][y] = d + val[i];
q.push((Node){y,i,d + val[i]});
}
}
}
}
bool dfs1(int x)
{
p[++top] = x,h[x] = top;
if(x == n) return true;
for(int i = head[x],y;i;i = nxt[i])
{
y = to[i];
if(!vis[i]) continue;
id[top] = i / 2;
if(dfs1(y)) return true;
}
top--,h[x] = 0;
return false;
}
void dfs2(int x)
{
for(int i = head[x],y;i;i = nxt[i])
{
y = to[i];
if(!vis[i]) continue;
if(!h[y]) h[y] = h[x];
dfs2(y);
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i = 1,a,b,c;i <= m;i++)
{
scanf("%d%d%d",&a,&b,&c);
add_(a,b,c),add_(b,a,c);
w[i] = c;
}
for(int i = m;i >= 1;i--) w[i] = max(w[i],w[i + 1]);
dijkstra(n,0),dijkstra(1,1);
dfs1(1),dfs2(1);
for(int i = 1;i <= n;i++)
for(int j = head[i];j;j = nxt[j])
if(!vis[j] && h[i] < h[to[j]])
{
ll tmp = dis[1][i] + dis[0][to[j]] + val[j];
v1[h[i]].pb(tmp),v2[h[to[j]]].pb(tmp);
}
for(int i = 1;i < top;i++)
{
for(ll v : v1[i]) s.insert(v);
for(ll v : v2[i]) s.erase(s.lower_bound(v));
ans = max(ans,min(dis[1][n] + w[id[i] + 1],s.empty() ? INF : (*s.begin())));
}
printf("%lld\n",ans);
return 0;
}