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

题面

在这里插入图片描述

题解

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

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

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

类似这样:

在这里插入图片描述

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

\(dis_u\) 表示最短路树上 \(T\)\(u\) 的路径长。考虑如何维护上面蓝色路径的最小值。把这段路程表示出来:

\[dis_x-dis_u+w_{(x,y)}+dis_y \]

那么我们直接维护 \(dis_x+w_{(x,y)}+dis_y\) 的最小值即可。

对于每一个点 \(u\),用堆维护其子树内 \(dis_x+w_{(x,y)}+dis_y\) 的最小值。

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

最多合并 \(n-1\) 次(\(n-1\) 条边),所以维护这个的过程时间不会超过 \(O(n\log n)\)

不妨设 \(g_u\) 表示删掉边 \((u,\textit{fa}_u)\) 之后到达 \(T\) 的最小值。(\(g_u\) 通过上面的过程求出)

\(dp_u\) 表示 \(S=u\) 时的答案,用 \(v\) 更新 \(u\) 时的转移为:

\[dp_u =\min_{(u,v)}\left(\max\left(dp_v+w_{(u, v)}, \begin{cases}g_u&v=\textit{fa}_u\\dis_u&v\neq \textit{fa}_u\end{cases}\right)\right)\]

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

时间复杂度 \(O((n+m)\log n)\)

代码如下:

#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 @ 2022-10-30 12:38  ez_lcw  阅读(28)  评论(0编辑  收藏  举报