[BZOJ1576][Usaco2009 Jan]安全路经Travel
[BZOJ1576][Usaco2009 Jan]安全路经Travel
试题描述
输入
* 第一行: 两个空格分开的数, N和M
* 第2..M+1行: 三个空格分开的数a_i, b_i,和t_i
输出
* 第1..N-1行: 第i行包含一个数:从牛棚_1到牛棚_i+1并且避免从牛棚1到牛棚i+1最短路经上最后一条牛路的最少的时间.如果这样的路经不存在,输出-1.
输入示例
4 5 1 2 2 1 3 2 3 4 4 3 2 1 2 4 3
输出示例
3 3 6
数据规模及约定
见“试题描述”
题解
首先跑一边最短路把最短路树建出来,然后对于一条非树边 u -> v,令 c = lca(u, v)(最近公共祖先)那么 v 到 c(包括 v 但不含 c)的路径上任意一个节点 x 都可以通过 1 -> u -> v -> x 的方式到达,长度即为 Dep[u] + Dep[v] + L - Dep[x],那么我们只需要维护这个最小的 Dep[u] + Dep[v] + L 即可(Dep[i] 表示 1 到节点 i 的最短路长度),然后对于 u 到 c 的路径也进行类似的处理。
那么就可以树链剖分 + 线段树维护啦,线段树支持区间取 min,以及单点询问。
注意区间取 min 标记下传的时候标记也得取一下 min,而不是直接赋值。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cctype> #include <algorithm> #include <queue> using namespace std; int read() { int x = 0, f = 1; char c = getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); } return x * f; } #define maxn 100010 #define maxm 200010 #define maxM 400010 #define oo 2147483647 int n, M; struct Graph { int m, head[maxn], nxt[maxM], to[maxM], dist[maxM]; Graph(): m(0) { memset(head, 0, sizeof(head)); } void AddEdge(int a, int b, int c) { to[++m] = b; dist[m] = c; nxt[m] = head[a]; head[a] = m; swap(a, b); to[++m] = b; dist[m] = c; nxt[m] = head[a]; head[a] = m; return ; } } gra, tree; int d[maxn], fa[maxn], fae[maxn]; bool vis[maxn], tag[maxM]; struct Node { int u, d; Node() {} Node(int _, int __): u(_), d(__) {} bool operator < (const Node& t) const { return d > t.d; } }; priority_queue <Node> Q; void ShortPath() { for(int i = 1; i <= n; i++) d[i] = oo; d[1] = 0; Q.push(Node(1, 0)); while(!Q.empty()) { int u = Q.top().u; Q.pop(); if(vis[u]) continue; vis[u] = 1; for(int e = gra.head[u]; e; e = gra.nxt[e]) if(d[gra.to[e]] > d[u] + gra.dist[e]) { d[gra.to[e]] = d[u] + gra.dist[e]; fa[gra.to[e]] = u; fae[gra.to[e]] = e; if(!vis[gra.to[e]]) Q.push(Node(gra.to[e], d[gra.to[e]])); } } return ; } int son[maxn], dep[maxn], Dep[maxn], siz[maxn], top[maxn], clo, pos[maxn]; void build(int u) { siz[u] = 1; for(int e = tree.head[u]; e; e = tree.nxt[e]) if(tree.to[e] != fa[u]) { dep[tree.to[e]] = dep[u] + 1; Dep[tree.to[e]] = Dep[u] + tree.dist[e]; build(tree.to[e]); siz[u] += siz[tree.to[e]]; if(!son[u] || siz[son[u]] < siz[tree.to[e]]) son[u] = tree.to[e]; } return ; } void gett(int u, int tp) { top[u] = tp; pos[u] = ++clo; if(son[u]) gett(son[u], tp); for(int e = tree.head[u]; e; e = tree.nxt[e]) if(tree.to[e] != fa[u] && tree.to[e] != son[u]) gett(tree.to[e], tree.to[e]); return ; } int lca(int a, int b) { int f1 = top[a], f2 = top[b]; while(f1 != f2) { if(dep[f1] < dep[f2]) swap(f1, f2), swap(a, b); a = fa[f1]; f1 = top[a]; } return dep[a] < dep[b] ? a : b; } int minv[maxn<<2], setv[maxn<<2]; void pushdown(int o, int l, int r) { if(!setv[o]) return ; if(l == r){ setv[o] = 0; return ; } int lc = o << 1, rc = lc | 1; if(!setv[lc]) setv[lc] = setv[o]; else setv[lc] = min(setv[lc], setv[o]); if(!setv[rc]) setv[rc] = setv[o]; else setv[rc] = min(setv[rc], setv[o]); minv[lc] = min(minv[lc], setv[o]); minv[rc] = min(minv[rc], setv[o]); setv[o] = 0; return ; } void update(int o, int l, int r, int ql, int qr, int v) { pushdown(o, l, r); if(ql <= l && r <= qr) { minv[o] = min(minv[o], v); setv[o] = v; return ; } int mid = l + r >> 1, lc = o << 1, rc = lc | 1; if(ql <= mid) update(lc, l, mid, ql, qr, v); if(qr > mid) update(rc, mid + 1, r, ql, qr, v); minv[o] = min(minv[lc], minv[rc]); return ; } int query(int o, int l, int r, int x) { pushdown(o, l, r); if(l == r) return minv[o]; int mid = l + r >> 1, lc = o << 1, rc = lc | 1; if(x <= mid) return query(lc, l, mid, x); return query(rc, mid + 1, r, x); } void modify(int u, int t, int val) { // printf("modify: %d to %d %d\n", u, t, val); while(top[u] != top[t]) { update(1, 1, n, pos[top[u]], pos[u], val); u = fa[top[u]]; } if(pos[t] < pos[u]) update(1, 1, n, pos[t] + 1, pos[u], val); return ; } int main() { n = read(); M = read(); for(int i = 1; i <= M; i++) { int a = read(), b = read(), c = read(); gra.AddEdge(a, b, c); } ShortPath(); for(int i = 2; i <= n; i++) { tree.AddEdge(i, fa[i], gra.dist[fae[i]]); // printf("tree: %d %d\n", i, fa[i]); tag[fae[i]] = 1; if(fae[i] & 1) tag[fae[i]+1] = 1; else tag[fae[i]-1] = 1; } build(1); gett(1, 1); for(int i = 1; i <= (n << 2); i++) minv[i] = oo; for(int u = 1; u <= n; u++) for(int e = gra.head[u]; e; e = gra.nxt[e]) if(!tag[e]) { int v = gra.to[e], c = lca(u, v); if(v != c) modify(v, c, Dep[u] + Dep[v] + gra.dist[e]); } for(int i = 2; i <= n; i++) { int tmp = query(1, 1, n, pos[i]); // printf("%d tmp: %d\n", i, tmp); printf("%d\n", tmp < oo ? tmp - Dep[i] : -1); } return 0; }