[BZOJ1576][Usaco2009 Jan]安全路经Travel
[BZOJ1576][Usaco2009 Jan]安全路经Travel
试题描述
.jpg)
输入
* 第一行: 两个空格分开的数, 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;
}

浙公网安备 33010602011771号