BZOJ1576 [Usaco2009 Jan]安全路经Travel

首先用Dijkstra做出最短路生成树,设dis[p]为1到p点的最短路长度

对于一条不在生成树上的边u -> v,不妨设fa为u、v的lca

则一fa到v的路径上的任意点x都可以由u达到,走的方式是1 -> fa -> u -> v -> x,dis‘[x] = dis[u] + dis(u, v) + dis[v] - dis[x]

于是可以用dis[u] + dis(u, v) + dis[v]更新fa到v的路径上的所有点

链剖一下,线段树lazytag就好了,连pushdown都不需要

  1 /**************************************************************
  2     Problem: 1576
  3     User: rausen
  4     Language: C++
  5     Result: Accepted
  6     Time:2276 ms
  7     Memory:16636 kb
  8 ****************************************************************/
  9
 10 #include <cstdio>
 11 #include <algorithm>
 12 #include <queue>
 13
 14 using namespace std;
 15 const int N = 1e5 + 5;
 16 const int M = 2e5 + 5;
 17 const int inf = 1e9;
 18
 19 struct edge {
 20     int next, to, v, vis;
 21     edge(int _n = 0, int _t = 0, int _v = 0, int __v = 0) : next(_n), to(_t), v(_v), vis(__v) {}
 22 } e[M << 1];
 23
 24 struct heap_node {
 25     int v, e, to;
 26     heap_node(int _v = 0, int _e = 0, int _t = 0) : v(_v), e(_e), to(_t) {}
 27
 28     inline bool operator < (const heap_node &p) const {
 29         return v > p.v;
 30     }
 31 };
 32
 33 struct tree_node {
 34     int fa, sz, dep;
 35     int top, son, e;
 36     int w;
 37 } tr[N];
 38
 39 struct seg_node {
 40     seg_node *ls, *rs;
 41     int mn;
 42 } *seg_root, *seg_null, mempool[N << 2], *cnt_seg = mempool;
 43
 44 int n, m, cnt_q;
 45 int first[N], tot = 1;
 46 int dis[N];
 47 priority_queue <heap_node> h;
 48
 49 inline int read() {
 50     static int x;
 51     static char ch;
 52     x = 0, ch = getchar();
 53     while (ch < ‘0‘ || ‘9‘ < ch)
 54         ch = getchar();
 55     while (‘0‘ <= ch && ch <= ‘9‘) {
 56         x = x * 10 + ch - ‘0‘;
 57         ch = getchar();
 58     }
 59     return x;
 60 }
 61
 62 inline void Add_Edges(int x, int y, int v) {
 63     e[++tot] = edge(first[x], y, v), first[x] = tot;
 64     e[++tot] = edge(first[y], x, v), first[y] = tot;
 65 }
 66
 67 inline void add_to_heap(int p) {
 68     static int x;
 69     for (x = first[p]; x; x = e[x].next)
 70         if (dis[e[x].to] == inf)
 71             h.push(heap_node(e[x].v + dis[p], x, e[x].to));
 72 }
 73
 74 void Dijkstra(int S) {
 75     int p;
 76     for (p = 1; p <= n; ++p) dis[p] = inf;
 77     while (!h.empty()) h.pop();
 78     dis[S] = 0, add_to_heap(S);
 79     while (!h.empty()) {
 80         if (dis[h.top().to] != inf) {
 81             h.pop();
 82             continue;
 83         }
 84         p = h.top().to;
 85         dis[p] = h.top().v, e[tr[p].e = h.top().e].vis = 1;
 86         h.pop(), add_to_heap(p);
 87     }
 88 }
 89
 90 #define y e[x].to
 91 #define Son tr[p].son
 92 void dfs(int p) {
 93     int x;
 94     tr[p].sz = 1;
 95     for (x = first[p]; x; x = e[x].next)
 96         if (tr[y].e == x) {
 97             tr[y].dep = tr[p].dep + 1;
 98             dfs(y);
 99             tr[p].sz += tr[y].sz;
100             if (Son == 0 || tr[y].sz > tr[Son].sz) Son = y;
101         }
102 }
103
104 void DFS(int p) {
105     int x;
106     tr[p].w = ++cnt_q;
107     if (Son)
108         tr[Son].top = tr[p].top, DFS(Son);
109         for (x = first[p]; x; x = e[x].next)
110             if (y != Son && tr[y].e == x)
111                 tr[y].top = y, DFS(y);
112 }
113 #undef y
114 #undef Son
115
116 #define mid (l + r >> 1)
117 #define Ls p -> ls
118 #define Rs p -> rs
119 #define Mn p -> mn
120 inline void seg_make_null(seg_node *&p) {
121     p = cnt_seg, Ls = Rs = p, Mn = inf;
122 }
123
124 inline void seg_new(seg_node *&p) {
125     p = ++cnt_seg, *p = *seg_null;
126 }
127
128 void seg_build(seg_node *&p, int l, int r) {
129     seg_new(p);
130     if (l == r) return;
131     seg_build(Ls, l, mid), seg_build(Rs, mid + 1, r);
132 }
133
134 void seg_modify(seg_node *p, int l, int r, int L, int R, int v) {
135     if (L <= l && r <= R) {
136         Mn = min(Mn, v);
137         return;
138     }
139     if (L <= mid) seg_modify(Ls, l, mid, L, R, v);
140     if (mid + 1 <= R) seg_modify(Rs, mid + 1, r, L, R, v);
141 }
142
143 int seg_query(seg_node *p, int l, int r, int pos) {
144     if (l == r) return Mn;
145     return min(Mn, pos <= mid ? seg_query(Ls, l, mid, pos) : seg_query(Rs, mid + 1, r, pos));
146 }
147 #undef mid
148 #undef Ls
149 #undef Rs
150 #undef Mn
151
152 int get_lca(int x, int y) {
153     while (tr[x].top != tr[y].top) {
154         if (tr[tr[x].top].dep < tr[tr[y].top].dep) swap(x, y);
155         x = tr[tr[x].top].fa;
156     }
157     if (tr[x].dep < tr[y].dep) swap(x, y);
158     return y;
159 }
160
161 void work_modify(int x, int fa, int v) {
162     while (tr[x].top != tr[fa].top) {
163         seg_modify(seg_root, 1, n, tr[tr[x].top].w, tr[x].w, v);
164         x = tr[tr[x].top].fa;
165     }
166     seg_modify(seg_root, 1, n, tr[fa].w + 1, tr[x].w, v);
167 }
168
169 int main() {
170     int i, x, y, z, fa, tmp;
171     n = read(), m = read();
172     for (i = 1; i <= m; ++i) {
173         x = read(), y = read(), z = read();
174         Add_Edges(x, y, z);
175     }
176     Dijkstra(1);
177     for (i = 2; i <= n; ++i)
178         tr[i].fa = e[tr[i].e ^ 1].to;
179     dfs(1);
180     tr[1].top = 1, DFS(1);
181     seg_make_null(seg_null);
182     seg_build(seg_root, 1, n);
183 #define y e[x].to
184     for (i = 1; i <= n; ++i)
185         for (x = first[i]; x; x = e[x].next)
186             if (!e[x].vis) work_modify(y, get_lca(i, y), dis[i] + dis[y] + e[x].v);
187 #undef y
188     for (i = 2; i <= n; ++i) {
189         tmp = seg_query(seg_root, 1, n, tr[i].w);
190         printf("%d\n", tmp == inf ? -1 : tmp - dis[i]);
191     }
192     return 0;
193 }

时间: 2024-12-23 16:03:50

BZOJ1576 [Usaco2009 Jan]安全路经Travel的相关文章

bzoj 1576: [Usaco2009 Jan]安全路经Travel 树链剖分

1576: [Usaco2009 Jan]安全路经Travel Time Limit: 10 Sec  Memory Limit: 64 MB Submit: 665  Solved: 227[Submit][Status] Description Input * 第一行: 两个空格分开的数, N和M * 第2..M+1行: 三个空格分开的数a_i, b_i,和t_i Output * 第1..N-1行: 第i行包含一个数:从牛棚_1到牛棚_i+1并且避免从牛棚1到牛棚i+1最短路经上最后一条牛

【BZOJ 1576】 [Usaco2009 Jan]安全路经Travel

1576: [Usaco2009 Jan]安全路经Travel Time Limit: 10 Sec  Memory Limit: 64 MB Submit: 676  Solved: 231 [Submit][Status] Description Input * 第一行: 两个空格分开的数, N和M * 第2..M+1行: 三个空格分开的数a_i, b_i,和t_i Output * 第1..N-1行: 第i行包含一个数:从牛棚_1到牛棚_i+1并且避免从牛棚1到牛棚i+1最短路经上最后一条

[Usaco2009 Jan]安全路经Travel BZOJ1576 Dijkstra+树链剖分+线段树

分析: Dijkstra求最短路树,在最短路树上进行操作,详情可见上一篇博客:http://www.cnblogs.com/Winniechen/p/9042937.html 我觉得这个东西不压行写出了有点丑...之后写了一个压行后更丑的... 附上压行后的代码: #include <cstdio> #include <algorithm> #include <cmath> #include <cstdlib> #include <cstring>

【BZOJ】1576 [Usaco2009 Jan]安全路经Travel

[算法]最短路树+(树链剖分+线段树)||最短路树+并查集 [题解] 两种方法的思想是一样的,首先题目限制了最短路树唯一. 那么建出最短路树后,就是询问对于每个点断掉父边后重新找路径的最小值,其它路径只能是这个点和其子树节点通过非树边到达非子树节点. 这样考虑很难统计,换个角度考虑每条非树边的影响. 一条非树边连接两个端点u,v,它们会有一个LCA,那么这条非树边就可以影响u~LCA和v~LCA两条链上的点. 这样依然不方便统计,因为两条链上每个点的影响各不相同,所以使用差分的思想. 定义一条非

[Usaco2009 Jan]安全路经Travel

安全路径 题意 Solution 嗯,首先既然不能经过最后一条边,那么我们考虑建出一个最短路树 然后非树边\(u,v,w\),只能影响到\(u->v\)这个路径上的点,因为只能往回跑 那么考虑怎么更新.一个显然的办法就是把边按照某种顺序排列,然后用并查集标记一下哪个点访问过了,可以做到\(O(n)\) 那么按照什么顺序排序呢?我们设一个点\(u\)到1的距离(即最短路长度)为\(dis_u\) 那么对于一条非树边\(u->v\),设路径上其中一个点为\(x\),那么这个"不经过最后一

【bzoj1576/Usaco2009 Jan】安全路经Travel——dijkstra+并查集

Description Input * 第一行: 两个空格分开的数, N和M * 第2..M+1行: 三个空格分开的数a_i, b_i,和t_i Output * 第1..N-1行: 第i行包含一个数:从牛棚_1到牛棚_i+1并且避免从牛棚1到牛棚i+1最短路经上最后一条牛路的最少的时间.如果这样的路经不存在,输出-1. Sample Input 4 5 1 2 2 1 3 2 3 4 4 3 2 1 2 4 3 输入解释: 跟题中例子相同 Sample Output 3 3 6 输出解释: 跟

1574: [Usaco2009 Jan]地震损坏Damage

1574: [Usaco2009 Jan]地震损坏Damage Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 425  Solved: 232[Submit][Status][Discuss] Description 农夫John的农场遭受了一场地震.有一些牛棚遭到了损坏,但幸运地,所有牛棚间的路经都还能使用. FJ的农场有P(1 <= P <= 30,000)个牛棚,编号1..P. C(1 <= C <= 100,000)条双向路经

bzoj3396[Usaco2009 Jan]Total flow 水流*

bzoj3396[Usaco2009 Jan]Total flow 水流 题意: 求无环图的最大流.边数≤700. 题解: 管它有没有环.注意本题的节点标号既有大写字母,也有小写字母. 代码: 1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <queue> 5 #define inc(i,j,k) for(int i=j;i<=k;i++) 6 #de

3396: [Usaco2009 Jan]Total flow 水流

3396: [Usaco2009 Jan]Total flow 水流 Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 179  Solved: 73[Submit][Status] Description Input 第1行输入N,之后N行每行描述一条水管,前两个英文字母表示水管的两端(大小写字母是不一样的),后一个整数表示水管的流量,流量不会超过1000. Output 一个整数,表示总流量. Sample Input 5 A B 3 B C 3