bzoj3694 最短路

Description

给出一个 \(n\) 个点 \(m\) 条边的无向图, \(n\) 个点的编号从 \(1\) ~ \(n\) ,定义源点为 \(1\) 。定义最短路树如下:从源点 \(1\) 经过边集 \(T\) 到任意一点 \(i\) 有且仅有一条路径,且这条路径是整个图 \(1\)\(i\) 的最短路径,边集 \(T\) 构成最短路树。 给出最短路树,求对于除了源点 \(1\) 外的每个点 \(i\) ,求最短路,要求不经过给出的最短路树上的 \(1\)\(i\) 的路径的最后一条边。

Input

第一行包含两个数 \(n\)\(m\) ,表示图中有 \(n\) 个点和 \(m\) 条边。

接下来 \(m\) 行,每行有四个数 \(a_i,b_i,l_i,t_i\) ,表示图中第 \(i\) 条边连接 \(a_i\)\(b_i\) 权值为 \(l_i\)\(t_i\)\(1\) 表示这条边是最短路树上的边, \(t_i\)\(0\) 表示不是最短路树上的边。

Output

输出 \(n-1\) 个数,第i个数表示从 \(1\)\(i+1\) 的要求的最短路。无法到达输出 \(-1\)

Sample

Sample Input

5 9
3 1 3 1
1 4 2 1
2 1 6 0
2 3 4 0
5 2 3 0
3 2 2 1
5 3 1 1
3 5 2 0
4 5 4 0

Sample Output

6 7 8 5

Solution

对于一条不在最短路树上的边,设它的端点为 \(u\)\(v\) ,长度为 \(w\) 。设 \(t=lca(u,v)\)

\(t\to u\) 上的一个点 \(x\) 可以通过 \(1\to t\to v \to u \to x\) 来到达,这样就不会经过最短路树上的边。

此时 \(ans[x] = dis[v] + w + dis[u] - dis[x]\) 。所以我们需要最小化 \(dis[v] + w + dis[u]\) 。可以用树链剖分来搞。

最开始把 \(lca\) 写假了还 \(\mathrm{wa}\) 了三次\(\mathrm{TAT}\)

#include<bits/stdc++.h>
using namespace std;
#define N 100001
#define rep(i, a, b) for (int i = a; i <= b; i++)
#define INF 0x7fffffff
inline int read() {
    int x = 0; char ch = getchar(); while (!isdigit(ch))  ch = getchar();
    while (isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar(); return x;
}
int n, m, head[N], tot, cnt, fa[N], dep[N], siz[N], son[N], dis[N], ind, into[N], outo[N], bl[N], val[N << 2], tag[N << 2];
struct edge { int v, w, next; }e[N << 1];
struct edge2 { int u, v, w; }eg[N];
inline void add(int u, int v, int w) { e[++tot].v = v, e[tot].w = w, e[tot].next = head[u], head[u] = tot; }
void dfs(int u) {
    siz[u] = 1;
    for (int i = head[u], v; i; i = e[i].next) if ((v = e[i].v) != fa[u]) {
        dep[v] = dep[u] + 1, fa[v] = u, dis[v] = dis[u] + e[i].w, dfs(v), siz[u] += siz[v];
        if (siz[v] > siz[son[u]]) son[u] = v;
    }
}
void dfs(int u, int top) {
    bl[u] = top, into[u] = ++ind;
    if (son[u]) dfs(son[u], top);
    for (int i = head[u], v; i; i = e[i].next) if ((v = e[i].v) != fa[u] && v != son[u]) dfs(v, v);
    outo[u] = ind;
}
inline int lca(int x, int y) {
    for (; bl[x] != bl[y]; dep[bl[x]]>dep[bl[y]] ? x = fa[bl[x]] : y = fa[bl[y]]);
    return dep[x]<dep[y] ? x : y;
}
#define ls rt << 1
#define rs ls | 1
#define mid (l + r >> 1)
#define Min(a, b) a = min(a, b)
void build(int rt, int l, int r) {
    tag[rt] = INF;
    if (l == r) { val[rt] = INF; return; }
    build(ls, l, mid), build(rs, mid + 1, r);
}
void pushDown(int rt, int l, int r) {
    if (tag[rt] == INF || l == r) return;
    Min(tag[ls], tag[rt]), Min(tag[rs], tag[rt]);
    if (mid == l) Min(val[ls], tag[rt]);
    if (mid + 1 == r) Min(val[rs], tag[rt]);
    tag[rt] = INF;
}
void update(int rt, int l, int r, int L, int R, int v) {
    pushDown(rt, l, r);
    if (l >= L && r <= R) {
        Min(tag[rt], v);
        if (l == r) Min(val[rt], v);
        return;
    }
    if (L <= mid) update(ls, l, mid, L, R, v);
    if (R > mid) update(rs, mid + 1, r, L, R, v);
}
void update(int u, int v, int w) {
    while (bl[u] ^ bl[v]) update(1, 1, n, into[bl[u]], into[u], w), u = fa[bl[u]];
    if (u ^ v) update(1, 1, n, into[v] + 1, into[u], w);
}
int ask(int rt, int l, int r, int p) {
    pushDown(rt, l, r);
    if (l == r) return val[rt];
    if (p <= mid) return ask(ls, l, mid, p);
    else return ask(rs, mid + 1, r, p);
}
int main() {
    n = read(), m = read();
    while (m--) {
        int u = read(), v = read(), w = read(), t = read();
        if (t) add(u, v, w), add(v, u, w);
        else eg[++cnt].u = u, eg[cnt].v = v, eg[cnt].w = w;
    }
    dfs(1), dfs(1, 1), build(1, 1, n);
    for (int i = 1; i <= cnt; i++) {
        int t = lca(eg[i].u, eg[i].v), x = dis[eg[i].u] + dis[eg[i].v] + eg[i].w;
        update(eg[i].u, t, x), update(eg[i].v, t, x);
    }
    for (int i = 2; i <= n; i++) {
        int t = ask(1, 1, n, into[i]);
        if (t ^ INF) printf("%d ", t - dis[i]);
        else printf("-1 ");
    }
    return 0;
}

转载于:https://www.cnblogs.com/aziint/p/8658267.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值