[bzoj1576] [Usaco2009 Jan]安全路经Travel

艾玛,突然发现,自己又有很久没写博客了,日常在bz上刷刷水,就来写一篇吧


题意

给你一张无向图,保证从一到每个点的最短路只有一条。
然后呢对于每个点删掉1到他的最短路上的最后一条边(就是这条路径上与他自己相连的那条边)后1到他的最短路的长度.


解法

我们可以发现,对于这张图,我们把从一到所有点的最短路拿出来后并在一起,就是一棵树了(并且这棵树是唯一的)。我们跑最短路时,最终松弛这个点的那个点就是他的父亲辣。

然后我们就可以考虑在这棵树上搞事情♂
我们发现,对于一个点u,删掉他的那一条边,这个树就分成两部分(树)了。我们设这两部分为{S,T}(1所在的那部分为S),那么u就是T的根.
并且可以发现它的新的最短路一定是
S>(s,t) (sS,t T)>T
这个比较好证明.
然后呢我们就很容易想到,枚举中间的这条路径(s,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;
}
posted @ 2016-11-10 16:26  DraZxlnDdt  阅读(110)  评论(0编辑  收藏  举报