zoukankan      html  css  js  c++  java
  • [USACO09JAN]安全出行Safe Travel 最短路,并查集

    题目描述

    Gremlins have infested the farm. These nasty, ugly fairy-like

    creatures thwart the cows as each one walks from the barn (conveniently located at pasture_1) to the other fields, with cow_i traveling to from pasture_1 to pasture_i. Each gremlin is personalized and knows the quickest path that cow_i normally takes to pasture_i. Gremlin_i waits for cow_i in the middle of the final cowpath of the quickest route to pasture_i, hoping to harass cow_i.

    Each of the cows, of course, wishes not to be harassed and thus chooses an at least slightly different route from pasture_1 (the barn) to pasture_i.

    Compute the best time to traverse each of these new not-quite-quickest routes that enable each cow_i that avoid gremlin_i who is located on the final cowpath of the quickest route from pasture_1 to

    pasture_i.

    As usual, the M (2 <= M <= 200,000) cowpaths conveniently numbered 1..M are bidirectional and enable travel to all N (3 <= N <= 100,000) pastures conveniently numbered 1..N. Cowpath i connects pastures a_i (1 <= a_i <= N) and b_i (1 <= b_i <= N) and requires t_i (1 <= t_i <= 1,000) time to traverse. No two cowpaths connect the same two pastures, and no path connects a pasture to itself (a_i != b_i). Best of all, the shortest path regularly taken by cow_i from pasture_1 to pasture_i is unique in all the test data supplied to your program.

    By way of example, consider these pastures, cowpaths, and [times]:

    1--[2]--2-------+ 
    |       |       | 
    [2]     [1]     [3] 
    |       |       | 
    +-------3--[4]--4
    TRAVEL     BEST ROUTE   BEST TIME   LAST PATH 
    p_1 to p_2       1->2          2         1->2 
    p_1 to p_3       1->3          2         1->3 
    p_1 to p_4      1->2->4        5         2->4 

    When gremlins are present:

    TRAVEL     BEST ROUTE   BEST TIME    AVOID 
    p_1 to p_2     1->3->2         3         1->2 
    p_1 to p_3     1->2->3         3         1->3 
    p_1 to p_4     1->3->4         6         2->4 

    For 20% of the test data, N <= 200.

    For 50% of the test data, N <= 3000.

    TIME LIMIT: 3 Seconds

    MEMORY LIMIT: 64 MB

    Gremlins最近在农场上泛滥,它们经常会阻止牛们从农庄(牛棚_1)走到别的牛棚(牛_i的目的 地是牛棚_i).每一个gremlin只认识牛_i并且知道牛_i一般走到牛棚_i的最短路经.所以它 们在牛_i到牛棚_i之前的最后一条牛路上等牛_i. 当然,牛不愿意遇到Gremlins,所以准备找 一条稍微不同的路经从牛棚_1走到牛棚_i.所以,请你为每一头牛_i找出避免gremlin_i的最 短路经的长度.

    和以往一样, 农场上的M (2 <= M <= 200,000)条双向牛路编号为1..M并且能让所有牛到 达它们的目的地, N(3 <= N <= 100,000)个编号为1..N的牛棚.牛路i连接牛棚a_i (1 <= a_i <= N)和b_i (1 <= b_i <= N)并且需要时间t_i (1 <=t_i <= 1,000)通过. 没有两条牛路连接同样的牛棚,所有牛路满足a_i!=b_i.在所有数据中,牛_i使用的牛棚_1到牛 棚_i的最短路经是唯一的.

    输入输出格式

    输入格式:

    * Line 1: Two space-separated integers: N and M

    * Lines 2..M+1: Three space-separated integers: a_i, b_i, and t_i

    输出格式:

    * Lines 1..N-1: Line i contains the smallest time required to travel from pasture_1 to pasture_i+1 while avoiding the final cowpath of the shortest path from pasture_1 to pasture_i+1. If no such path exists from pasture_1 to pasture_i+1, output -1 alone on the line.

    输入输出样例

    输入样例#1: 
    4 5 
    1 2 2 
    1 3 2 
    3 4 4 
    3 2 1 
    2 4 3 
    
    输出样例#1: 
    3 
    3 
    6 

    提交地址 : Luogu2934

    题意:
    给你一些点, 他们与节点1的最短路的最后一条边不可走, 求每一个点到1的最短距离;

    这道题很折磨人...

    首先我们考虑一颗最短路径树(就是1到每个节点的最短路径路过的边构成的集合);

    然后我们要找一个点对(x, y),且xy之间有边,xy不在最短路径树上,y在节点i的子树里,x不在节点i的子树里;

    这样,我们要找的节点i的"非最短路径",就是dis(x) + dis(y) - dis(i) + w(x, y);
    其中, dis代表节点到根节点的最短路径, w(x, y)代表x 到 y 的路径长度;

    我们发现 : dis(i) 是确定的, 所以我们要最小化dis(x) + dis(y) + w(x, y);

    我们可以用左偏树维护(才不会写) 想学戳这里

    我直接排序;

    选出不在最短路径树上的边, 按dis(x) + dis(y) + w(x, y)最小sort一遍;

    然后更新答案;

    我们接着想 : 一个点如果已经被更新, 那么一定是最优的, 为什么, 因为他的dis值确定, 而我们已经对以上那一坨式子的值按由小到大排序,所以我们第一次更新一定是他的最优值;
    所以我们可以用并查集维护, 确保他们只被更新一次;

    那么我们再想 : 一个点对(x, y),能更新那些点的ans呢?
    能更新到x -> LCA(x, y) 和 y -> LCA(x, Y) 的所有点;
    为什么?
    因为 边 x-y 一定不是点i的父亲边 (题面说了), 如果我们更新到LCA(x, y) 那么x, y就在同一个子树里了;
    而我们能更新i的前提是x, y不在同一子树里;
    所以我们就像跳LCA一样一直往上跳, 一路更新答案, 然后修改并查集, 直到调到LCA;

    应该比较好懂;

    卡SPFA?差评!!

    代码奉上:
    //By zZhBr
    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <queue>
    #include <cstring>
    #include <queue>
    using namespace std;
    const int N = 100010;
    const int M = 400010;
    
    inline int read()
    {
        int res = 0;
        bool flag = 0;
        char c = getchar();
        while (c < '0' or c > '9')
        {
            if (c == '-') flag = 1;
            c = getchar();
        }
        while (c >= '0' and c <= '9')
        {
            res = res * 10 + c - '0';
            c = getchar();
        }
        return flag ? -res : res;
    }
    
    
    int n, m;
    int A[M], B[M], C[M];
    int what[M];//第几条边属于的编号 what[i] = what[i^1];
    int is[M]; //记录最短路径中第i个点的入边 
    bool itis[M];//记录是否是最短路径树的树边 
    int fafa[N];//记录在最短路径树中一个节点的父亲
    int fa[N];//并查集
    int ans[N];//记录答案 
    
    inline int Find(int x) 
    {
        return x == fa[x] ? x : fa[x] = Find(fa[x]);
    }
    
    struct edge
    {
        int nxt;
        int to;
        int val;
    }ed[M];
    
    int head[N], cnt;
    
    inline void add(int x, int y, int z, int gg)
    {
        cnt++;
        ed[cnt].nxt = head[x];
        ed[cnt].to = y;
        ed[cnt].val = z;
        head[x] = cnt;
        what[cnt] = gg;
    }
    
    inline void add(int x, int y)
    {
        cnt++;
        ed[cnt].nxt = head[x];
        ed[cnt].to = y;
        head[x] = cnt;
    }
    
    struct date
    {
        int x;
        int y;
        int w;
    //    date(){}
    //    date(int xx, int yy, int ww){x = xx, y = yy, w = ww;}
    }cf[M];
    
    bool cmp(date a, date b)
    {
        return a.w < b.w;
    }
    
    void dfs(int x) //简陋的寻找父亲的函数 
    {
        for (register int i = head[x] ;i ; i = ed[i].nxt)
        {
            int to = ed[i].to;
            if (to == fafa[x]) continue;
            fafa[to] = x;
            dfs(to);
        }
    }
    
    /*.............................Dijkstra...................................*/
    struct dij{int x, w;};
    
    bool operator <(const dij &a,const dij &b)
    {
        return a.w > b.w;
    }
    
    int dis[N];
    bool vis[N];
    
    inline void Dijkstra(int haha)
    {
        memset(dis, 0x3f, sizeof dis);
        
        priority_queue < dij > q;
        
        dis[haha] = 0;
        q.push((dij){haha, 0});
        
        while (!q.empty())
        {
            dij t = q.top(); q.pop();
            int x = t.x;
            if (vis[x]) continue;
            vis[x] = 1;
            for (register int i = head[x] ; i ; i = ed[i].nxt)
            {
                int to = ed[i].to;
                if (!vis[to] and dis[to] > dis[x] + ed[i].val) //!
                {
                    is[to] = what[i];
                    dis[to] = dis[x] + ed[i].val;
                    q.push((dij){to, dis[to]});
                }
            }
        }
    }
    /*.................................End..............................................*/
    
    
    int main()
    {    
        n = read(), m = read();
        
        for (register int i = 1 ; i <= m ; i ++)
        {
            A[i] = read(), B[i] = read(), C[i] = read();
            add(A[i], B[i], C[i], i);
            add(B[i], A[i], C[i], i);
        }
        
        Dijkstra(1);
        
        
        cnt = 0;
        memset(head, 0, sizeof head);
        
    //    for (int i=2;i<=n;i++)cout<<is[i]<<endl;
        
        for (register int i = 2 ; i <= n ; i ++)
        {
            int x = is[i];
            add(A[x], B[x]);
            add(B[x], A[x]);
            itis[x] = 1;
        }
        
        dfs(1);
        
        int num = 0;
        for (register int i = 1 ; i <= m ; i ++) //该搞非树边了 
        {
            if (itis[i]) continue;
        //    printf("%d
    ", i);
            cf[++num] = (date) {A[i], B[i], dis[A[i]] + dis[B[i]] + C[i]};
        }
        
        sort (cf + 1, cf + 1 + num, cmp);
        
        for (register int i = 1 ; i <= n ; i ++) fa[i] = i, ans[i] = -1;
        
        for (register int i = 1 ; i <= num ; i ++)
        {
            int x = cf[i].x, y = cf[i].y;
            x = Find(x), y = Find(y);
            
            //printf("x = %d, y = %d
    ", x, y);
            while (x != y)
            {
                if (dis[x] < dis[y]) swap(x, y);
                ans[x] = cf[i].w - dis[x];
                fa[x] = fafa[x];
                x = Find(x);
            }
            
        }
        
        for (register int i = 2 ; i <= n ; i ++)
        {
            printf("%d
    ", ans[i]);
        }
        return 0;
        
    }
    
    zZhBr
    
    
    
     

     可以转载, 但请注明地址谢谢;

    
    
  • 相关阅读:
    JS中常用的小代码整理
    (转)jquery each解析数组 json xml
    <转>如何做一个好的前端重构工程师
    js代码收集(1)
    firefox与ie 的javascript区别
    js模板渲染
    .NET平台下WEB应用程序的部署(安装数据库和自动配置)
    关于项目管理的思考
    随机数的生成(Asp.Net,C#)
    一套.net窗体身份验证方案(解决了防止用户重复登陆,session超时等问题)
  • 原文地址:https://www.cnblogs.com/BriMon/p/9069469.html
Copyright © 2011-2022 走看看