[bzoj1576] [Usaco2009 Jan]安全路经Travel
艾玛,突然发现,自己又有很久没写博客了,日常在bz上刷刷水,就来写一篇吧
题意
给你一张无向图,保证从一到每个点的最短路只有一条。
然后呢对于每个点删掉1到他的最短路上的最后一条边(就是这条路径上与他自己相连的那条边)后1到他的最短路的长度.
解法
我们可以发现,对于这张图,我们把从一到所有点的最短路拿出来后并在一起,就是一棵树了(并且这棵树是唯一的)。我们跑最短路时,最终松弛这个点的那个点就是他的父亲辣。
然后我们就可以考虑在这棵树上搞事情♂
我们发现,对于一个点u,删掉他的那一条边,这个树就分成两部分(树)了。我们设这两部分为{S,T}(1所在的那部分为S),那么u就是T的根.
并且可以发现它的新的最短路一定是
这个比较好证明.
然后呢我们就很容易想到,枚举中间的这条路径
那么从1到s的最短距离就是dis[s]了,从t到我们选的那个点的最短距离为dis[t]-dis[u].总距离为w(s,t)+dis[s]+dis[t]-dis[u]
然后在a到b的路径上的所有点(除了他们的lca)都可以看成上面说的u,然后我们就可以更新这些点的答案(具体操作就像暴力爬lca)。但是这样的复杂度是o(mn)的.QAQ
我们就可以考虑把所有边(a,b)按dis[a]+dis[b]+w(a,b)从小到大来排序。
这样的话,我们就可以发现,如果一个点,它的答案被更新了,那么以后就再也不会被更新.然后呢我们就可以将并查集将这个点和他上面被用过的点并起来(也就是说我们要保证对于每个集合,就只有他的代表元没有被更新过)。然后每次爬就直接爬到集合的代表元。这样的复杂的是(nα(n)+m)的。这样就解决了QAQ。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<iostream>
#include<queue>
using namespace std;
const int N = 1e5 + 7, M = 2e5 + 7;
struct item {
int w, id;
item () {}
item (int w,int id) : w (w), id (id) {}
bool operator < (const item &rhs) const { return w > rhs.w; }
};
priority_queue<item>Q;
int dis[N], fa[N], ans[N], cnt, n, m, f[N], rank[N]; bool vis[N];
char B[1 << 12], *S = B, *T = B;
char getchar2 () { return S == T && (T = (S = B) + fread (B, 1, 1 << 12, stdin), S == T) ? -1 : *S++; }
char b[1 << 12], *t = b;
void putchar2 (char A) { if (t - b == 1 << 12) fwrite (b, 1, 1 << 12, stdout), t = b; *t++ = A; }
void G (int &num) {
static char a;
for (a = getchar2 (); a > '9' || a < '0'; a = getchar2 ()) ;
for (num = 0; a >= '0' && a <= '9'; a = getchar2 ()) num = (num << 3) + (num << 1) + a - '0';
}
struct Edge {
int to, cost;
Edge *next;
Edge (int to, int cost, Edge *next) : to (to), cost (cost), next (next) {}
Edge () {}
}*head[N], meme[M << 1], *pis = meme;
struct e {
int u, v, w;
bool operator < (const e &rhs) const { return w < rhs.w; }
}e[M];
void AddEdge () {
static int u, v, w;
G (u); G (v); G (w);
++cnt; e[cnt].u = u; e[cnt].v = v; e[cnt].w = w;
head[u] = new (pis++) Edge (v, w, head[u]);
head[v] = new (pis++) Edge (u, w, head[v]);
}
void Init () {
G (n); G (m);
for (int i = 1; i <= m; ++i) AddEdge ();
}
void Dijkstra (int u) {
memset (dis, 0x3f, sizeof (dis));
Q.push (item (dis[u] = 0, u));
while (!Q.empty ()) {
u = Q.top ().id; Q.pop ();
for (Edge *now = head[u]; now; now = now -> next)
if (!vis[now -> to] && dis[u] + now -> cost < dis[now -> to]) {
dis[now -> to] = dis[u] + now -> cost;
fa[now -> to] = u;
Q.push (item (dis[now -> to], now -> to));
}
}
}
int find (int x) { return x == f[x] ? x : f[x] = find (f[x]); }
void Merge (int x, int y) { f[find (x)] = find (y); }
void print (int x) {
static char stk[10]; static bool fl; static int top;
if (x < 0) fl = true, x = -x; else fl = false;
top = 0; while (x) stk[++top] = x % 10 + '0', x /= 10;
if (fl) putchar2 ('-'); while (top) putchar2 (stk[top--]); putchar2 ('\n');
}
void Solve () {
for (int i = 1; i <= cnt; ++i) e[i].w += dis[e[i].u] + dis[e[i].v];
std :: sort (e + 1, e + cnt + 1);
memset (ans, -1, sizeof (ans));
for (int i = 1; i <= n; ++i) f[i] = i;
int u, v, w;
for (int i = 1; i <= cnt; ++i) {
u = e[i].u; v = e[i].v; w = e[i].w;
if (u == fa[v] || v == fa[u]) continue ;
u = find (u); v = find (v);
while (u != v) {
if (dis[u] < dis[v]) u ^= v ^= u ^= v;
ans[u] = w - dis[u];
Merge (u, fa[u]);
u = find (u);
}
}
for (int i = 2; i <= n; ++i) print (ans[i]);
}
void IO () {
freopen ("1576.in", "r", stdin);
freopen ("1576.out", "w", stdout);
}
int main () {
IO ();
Init ();
Dijkstra (1);
Solve ();
fwrite (b, 1, t - b, stdout);
return 0;
}