【XSY3888】传送门(最短路树,可并堆,dp)

题面

在这里插入图片描述

题解

假设 ST 路径上一条边 (u,v) 被删掉之后最短路会如何变化。

建出以 T 为根的最短路树,如果 (u,v) 不在树上,那么我们直接从 u 沿着最短路树走到 T 即可。

否则,如果 (u,v) 在最短路树上,那么 v 一定是 u 的父亲。那么我们需要从 u 走到 u 子树内的任意一个点 x,然后再从 x 经过一条非树边跳到一个在 u 子树外的点 y,然后再沿最短路树走到 T

类似这样:

在这里插入图片描述

(其中黑色虚线边表示的是非树边,红叉边表示被删掉的边,蓝色表示路径)

disu 表示最短路树上 Tu 的路径长。考虑如何维护上面蓝色路径的最小值。把这段路程表示出来:

disxdisu+w(x,y)+disy

那么我们直接维护 disx+w(x,y)+disy 的最小值即可。

对于每一个点 u,用堆维护其子树内 disx+w(x,y)+disy 的最小值。

合并的时候用左偏树即可。

最多合并 n1 次(n1 条边),所以维护这个的过程时间不会超过 O(nlogn)

不妨设 gu 表示删掉边 (u,fau) 之后到达 T 的最小值。(gu 通过上面的过程求出)

dpu 表示 S=u 时的答案,用 v 更新 u 时的转移为:

dpu=min(u,v)(max(dpv+w(u,v),{guv=faudisuvfau))

每次找到 dp 值最小的点来更新其它的点的 dp 值即可,类似 dijkstra。

时间复杂度 O((n+m)logn)

代码如下:

#include<bits/stdc++.h>
 
#define N 100010
#define M 200010
#define intree(v,u) (dfn[u]<=dfn[v]&&dfn[v]<=dfn[u]+size[u]-1)//判断是否在子树内
 
using namespace std;
 
struct data
{
    int u,s;
    data(){};
    data(int a,int b){u=a,s=b;}
    bool operator < (const data &a) const
    {
        return s>a.s;
    }
};
 
struct Heap//左偏树
{
    #define lc(u) ch[u][0]
    #define rc(u) ch[u][1]
    int node,ch[M][2],to[M],dis[M],val[M];
    int top,rubbish[M];
    int newnode(int y,int v)
    {
        int now;
        if(top)
        {
            now=rubbish[top--];
            lc(now)=rc(now)=dis[now]=0;
        }
        else now=++node;
        to[now]=y,val[now]=v;
        return now;
    }
    int merge(int x,int y)
    {
        if(!x||!y) return x+y;
        if(val[x]>val[y]) swap(x,y);
        rc(x)=merge(rc(x),y);
        if(dis[rc(x)]>dis[lc(x)]) swap(lc(x),rc(x));
        dis[x]=dis[rc(x)]+1;
        return x;
    }
    int pop(int u)
    {
        rubbish[++top]=u;
        return merge(lc(u),rc(u));
    }
}heap;
 
int n,m;
int cnt,head[N],nxt[M<<1],to[M<<1],w[M<<1];
int dis[N],fa[N];
int idx,dfn[N],size[N];
int root[N];
int dp[N],g[N];
bool vis[N];
 
vector<int>e[N];
priority_queue<data>q;
 
void adde(int u,int v,int wi)
{
    to[++cnt]=v;
    w[cnt]=wi;
    nxt[cnt]=head[u];
    head[u]=cnt;
}
 
void dijkstra()
{
    memset(dis,127,sizeof(dis));
    q.push(data(n,0));
    dis[n]=0;
    while(!q.empty())
    {
        data now=q.top();
        q.pop();
        if(vis[now.u]) continue;
        vis[now.u]=1;
        for(int i=head[now.u];i;i=nxt[i])
        {
            int v=to[i];
            if(dis[now.u]+w[i]<dis[v])
            {
                dis[v]=dis[now.u]+w[i];
                fa[v]=now.u;
                q.push(data(v,dis[v]));
            }
        }
    }
}
 
void dfs(int u)
{
    dfn[u]=++idx;
    size[u]=1;
    for(int i=0,siz=e[u].size();i<siz;i++)
    {
        int v=e[u][i];
        dfs(v);
        size[u]+=size[v];
        root[u]=heap.merge(root[u],root[v]);
    }
    for(int i=head[u];i;i=nxt[i])
    {
        int v=to[i];
        if(v==fa[u]||intree(v,u)) continue;
        root[u]=heap.merge(root[u],heap.newnode(v,dis[u]+w[i]+dis[v]));
    }
    while(root[u]&&intree(heap.to[root[u]],u))
        root[u]=heap.pop(root[u]);
    if(root[u]) g[u]=heap.val[root[u]]-dis[u];
}
 
void DP()
{
    memset(vis,0,sizeof(vis));
    memset(dp,127,sizeof(dp));
    q.push(data(n,0));
    dp[n]=0;
    while(!q.empty())
    {
        data now=q.top();
        q.pop();
        if(vis[now.u]) continue;
        vis[now.u]=1;
        for(int i=head[now.u];i;i=nxt[i])
        {
            int v=to[i];
            if(max(dp[now.u]+w[i],now.u==fa[v]?g[v]:dis[v])<dp[v])
            {
                dp[v]=max(dp[now.u]+w[i],now.u==fa[v]?g[v]:dis[v]);
                q.push(data(v,dp[v]));
            }
        }
    }
}
 
int main()
{
    memset(g,127,sizeof(g));
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        adde(u,v,w),adde(v,u,w);
    }
    dijkstra();
    for(int i=1;i<n;i++)
        e[fa[i]].push_back(i);
    dfs(n);
//  puts("I didn't MLE!!!");
//  return 0;
    DP();
    if(dp[1]!=dp[0]) printf("%d\n",dp[1]);
    else puts("-1");
    return 0;
}
/*
5 10
1 3 10
1 5 4
3 4 6
5 2 10
1 2 6
4 5 2
2 3 2
4 1 5
4 2 6
5 3 3
*/
posted @   ez_lcw  阅读(40)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示