[USACO09JAN]Safe Travel G

题目描述

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

输入格式

* 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.

题意翻译

【题目描述】

给定一张有 nn 个节点,mm 条边的无向图,对于任意的 ii(2\le i\le n2≤i≤n),请求出在不经过原来 11 节点到 ii 节点最短路上最后一条边的前提下,11 节点到 ii 节点的最短路。

特别地,保证 11 到任何一个点 ii 的最短路都是唯一的。

【输入格式】

第一行,两个整数 n,mn,m。

之后 mm 行,每行三个整数 a_i,b_i,t_iai​,bi​,ti​ 表示有一条 a_iai​ 到 b_ibi​,边权为 t_iti​ 的无向边。

【输出格式】

共 n-1n−1 行,第 ii 行表示 11 到 i+1i+1 在不经过原来 11 节点到 i+1i+1 节点最短路上最后一条边的前提下的最短路。

翻译贡献者:@cryozwq

输入输出样例

输入 #1复制

4 5 
1 2 2 
1 3 2 
3 4 4 
3 2 1 
2 4 3 

输出 #1复制

3 
3 
6 

题意: 给你一些点, 他们与节点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;

应该比较好懂;

代码奉上:

//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\n", 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\n", 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\n", ans[i]);
    }
    return 0;
    
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值