zoukankan      html  css  js  c++  java
  • 【XSY3888】传送门(最短路树,可并堆,dp)

    题面

    在这里插入图片描述

    题解

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

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

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

    类似这样:

    在这里插入图片描述

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

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

    d i s x − d i s u + w ( x , y ) + d i s y dis_x-dis_u+w_{(x,y)}+dis_y disxdisu+w(x,y)+disy

    那么我们直接维护 d i s x + w ( x , y ) + d i s y dis_x+w_{(x,y)}+dis_y disx+w(x,y)+disy 的最小值即可。

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

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

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

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

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

    d p u = min ⁡ ( u , v ) ( max ⁡ ( d p v + w ( u , v ) , { g u v = fa u d i s u v ≠ fa u ) ) dp_u =min_{(u,v)}left(maxleft(dp_v+w_{(u, v)}, egin{cases}g_u&v= extit{fa}_u\dis_u&v eq extit{fa}_uend{cases} ight) ight) dpu=(u,v)min(max(dpv+w(u,v),{gudisuv=fauv=fau))

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

    时间复杂度 O ( ( n + m ) log ⁡ n ) O((n+m)log n) 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
    ",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
    */
    
  • 相关阅读:
    服务器
    python的并发编程学习
    python的网络编程学习
    python的基础学习 模块
    pymysql基础操作
    查询简单练习
    数据准备
    数据库的三大设计范式
    数据库基础-建表约束
    mysql基础-基本语法
  • 原文地址:https://www.cnblogs.com/ez-lcw/p/14448623.html
Copyright © 2011-2022 走看看