【HNOI2014】道路堵塞
题面
题解
解法一
这个思路要基于以下一个结论:
当你删掉某条边\((x,x+1)\)时,最短路路线为:\(1\to x(\leq u)\to y(>u) \to n\),并且\(x\to y\)一定不会属于原最短路。
我们枚举删掉最短路上的哪条边,然后把这条边的\(s\)加进队列做SPFA,这个应该就是部分分。
优化:先把最短路上的所有边都删掉,然后从\(S\)到\(T\)开始恢复边,并把\(s\)丢进队列。然后每次就可以不清空\(dis\)数组了,因为你之前SPFA的结果你都可以调用以前的结果。
然后统计答案就是把走到的钦定最短路上的点的距离加上它到\(T\)的距离取个\(\min\)就好了,这个用堆就可以了。
时间复杂度\(\mathrm{O}(\mathrm{spfa})\)
而\(\mathrm{spfa}\)的复杂度是无法保证的,所以这个方法是假的。
解法二
分别从起点和终点开始求最短路图,可以证明删去一条边之后的最短路最多只会有一条边不在最短路图上。
设给出的最短路为\(v_1, v_2, \cdots, v_l\)。
枚举每条边\((a, b)\),那么经过这条边的最短路一定是形如
\(v_1 \to v_2 \to \cdots \to v_i \to \cdots \to a \to b \to \cdots \to v_j \to \cdots \to v_l\),找出满足条件的最小的\(i\)和最大的\(j\),
那么如果删去的边是\(v_i \to v_j\)的边,那么都可以用这条路径来替代。
于是就变成了区间取\(\min\),询问每一条边的值,用线段树即可。
时间复杂度\(\mathrm{O}(n \log_2n)\)
代码
解法一
#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#include<queue>
#define RG register
#define file(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define clear(x, y) memset(x, y, sizeof(x))
inline int read()
{
int data = 0, w = 1; char ch = getchar();
while(ch != '-' && (!isdigit(ch))) ch = getchar();
if(ch == '-') w = -1, ch = getchar();
while(isdigit(ch)) data = data * 10 + (ch ^ 48), ch = getchar();
return data * w;
}
const int maxn(200010), maxm(1000010);
struct edge { int next, to, dis; } e[maxm];
int head[maxn], e_num, n, m, L, vis[maxn];
int blk[maxn], sp[maxn], s[maxn], from[maxn];
int g[maxn], dis[maxn];
struct node { int to, val; };
inline bool operator < (const node &lhs, const node &rhs)
{ return lhs.val > rhs.val; }
std::priority_queue<node> heap;
inline void add_edge(int from, int to, int dis)
{
e[++e_num] = (edge) {head[from], to, dis};
head[from] = e_num;
}
void spfa(int S)
{
std::queue<int> q; q.push(S), vis[S] = 1;
while(!q.empty())
{
int x = q.front(); q.pop(); vis[x] = 0;
for(RG int i = head[x]; i; i = e[i].next)
{
if(blk[i]) continue;
int to = e[i].to;
if(dis[to] > dis[x] + e[i].dis)
{
dis[to] = dis[x] + e[i].dis;
if(from[to]) heap.push((node)
{from[to], dis[to] + g[from[to]]});
else if(!vis[to]) q.push(vis[to] = to);
}
}
}
}
int main()
{
n = read(), m = read(), L = read();
for(RG int i = 1, a, b, c; i <= m; i++)
a = read(), b = read(), c = read(),
add_edge(a, b, c);
for(RG int i = 1; i <= L; i++)
{
sp[i] = read(), blk[sp[i]] = 1;
from[s[i + 1] = e[sp[i]].to] = i + 1;
}
for(RG int i = L; i; i--) g[i] = g[i + 1] + e[sp[i]].dis;
memset(dis, 0x3f, sizeof dis);
dis[from[1] = s[1] = 1] = 0, spfa(1);
for(RG int i = 1; i <= L; i++)
{
while(!heap.empty() && heap.top().to <= i) heap.pop();
if(heap.empty()) puts("-1"); else printf("%d\n", heap.top().val);
dis[e[sp[i]].to] = dis[s[i]] + e[sp[i]].dis, spfa(s[i + 1]);
}
return 0;
}
解法二
#include<cstdio>
#include<cstring>
#include<cctype>
#include<climits>
#include<algorithm>
#include<queue>
#define RG register
#define file(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define clear(x, y) memset(x, y, sizeof(x))
inline int read()
{
int data = 0, w = 1; char ch = getchar();
while(ch != '-' && (!isdigit(ch))) ch = getchar();
if(ch == '-') w = -1, ch = getchar();
while(isdigit(ch)) data = data * 10 + (ch ^ 48), ch = getchar();
return data * w;
}
const int maxn(100010);
struct edge { int next, to; } e[maxn << 1];
int n, m, L, min[maxn << 2], head[maxn], e_num, vis[maxn];
std::pair<int, int> dis[2][maxn];
typedef std::pair<std::pair<int, int>, int> pair;
inline void add_edge(int from, int to)
{
e[++e_num] = (edge) {head[from], to};
head[from] = e_num;
}
struct node { int x, y, dis, opt; } E[maxn << 1];
std::priority_queue<pair, std::vector<pair>, std::greater<pair> > Q;
void Dijkstra(int S, int opt, int T)
{
Q.push(std::make_pair(dis[opt][S], S));
memset(vis, 0, sizeof vis);
for(RG int i = 1; i <= n; i++) if(E[i].opt)
Q.push(std::make_pair(dis[opt][E[i].y], E[i].y));
while(!Q.empty())
{
int x = Q.top().second; Q.pop();
if(vis[x] || x == T) continue;
vis[x] = 1;
for(RG int i = head[x]; i; i = e[i].next)
{
if(E[i].opt) continue;
std::pair<int, int> t =
std::make_pair(dis[opt][x].first + E[i].dis, dis[opt][x].second);
if(t < dis[opt][E[i].y]) dis[opt][E[i].y] = t,
Q.push(std::make_pair(t, E[i].y));
}
}
}
void build(int root = 1, int l = 1, int r = L)
{
min[root] = INT_MAX; if(l == r) return;
int mid = (l + r) >> 1, lson = root << 1, rson = lson | 1;
build(lson, l, mid); build(rson, mid + 1, r);
}
void modify(int ql, int qr, int v, int root = 1, int l = 1, int r = L)
{
if(ql <= l && r <= qr) return (void)(min[root] = std::min(min[root], v));
int mid = (l + r) >> 1, lson = root << 1, rson = lson | 1;
if(ql <= mid) modify(ql, qr, v, lson, l, mid);
if(mid < qr) modify(ql, qr, v, rson, mid + 1, r);
}
int query(int pos, int root = 1, int l = 1, int r = L)
{
if(l == r) return min[root];
int mid = (l + r) >> 1, lson = root << 1, rson = lson | 1;
if(pos <= mid) return std::min(min[root], query(pos, lson, l, mid));
else return std::min(min[root], query(pos, rson, mid + 1, r));
}
int main()
{
#ifndef ONLINE_JUDGE
file(cpp);
#endif
n = read(), m = read(), L = read();
for(RG int i = 1, a, b, c; i <= m; i++)
a = read(), b = read(), c = read(),
E[i] = (node) {a, b, c, 0}, add_edge(a, b);
for(RG int i = 1; i <= n; i++)
dis[0][i] = dis[1][i] = std::make_pair(INT_MAX >> 1, 0);
dis[0][1] = dis[1][n] = std::make_pair(0, 0);
for(RG int i = 1, x; i <= L; i++)
{
E[x = read()].opt = 1;
dis[0][E[x].y].first = dis[0][E[x].x].first + E[x].dis;
dis[1][E[x].y].second = -(dis[0][E[x].y].second = i);
}
for(RG int i = 1; i <= m; i++) if(E[i].opt)
dis[1][E[i].x].first = dis[0][n].first - dis[0][E[i].x].first;
Dijkstra(1, 0, n); memset(head, 0, sizeof head); e_num = 0;
for(RG int i = 1; i <= m; i++)
std::swap(E[i].x, E[i].y), add_edge(E[i].x, E[i].y);
Dijkstra(n, 1, 1); for(RG int i = 1; i <= m; i++) std::swap(E[i].x, E[i].y);
for(RG int i = 1; i <= n; i++) dis[1][i].second *= -1;
build();
for(RG int i = 1; i <= m; i++)
{
if(E[i].opt) continue;
std::pair<int, int> x = dis[0][E[i].x], y = dis[1][E[i].y];
if(x.second < y.second) modify(x.second + 1, y.second,
x.first + y.first + E[i].dis);
}
for(RG int i = 1; i <= L; i++)
{
int now = query(i); if(now >= (1 << 30)) now = -1;
printf("%d\n", now);
}
return 0;
}