樹剖Ⅰ-貨車運輸:樹剖+最大生成樹

這裏寫圖片描述
2017.10.19有一次膜你模擬賽,最後一道題是貨車運輸。
dx在比賽前兩分鐘剛A了這道題。。。我就GG了。。。
我只記得是最大生成樹+LCA。於是就碼,結果最大生成樹寫炸了,克魯斯卡爾調了1h才調出來。。。一直調到最後15min才調出來,然後畫了上面那幅圖。當時所有人都在嘲諷我說:你連最大生成樹都寫炸你別想A了。然而測評後,只有我和dxA了這道題233。
於是今天抽空寫篇博客來記錄一下最大生成樹。
其實重點還是樹剖。
先說最大生成樹。
把邊sort一遍後亂跑並查集就好了
但各種細節要維護好啊

  • inline int fnd(int x){ return x == f[x] ? x : f[x] = fnd(f[x]); }

    嗯反正很短背下來好了。。。
  • if(y == n - 1) break;
    n是點數m是邊數!一開始手賤打成m了,我說最大生成樹怎麼建的亂七八糟的

  • for(register int i = 0; i <= n; i++) f[i] = i;
    一定要賦初值啊!!!

  • f[f1] = f2;
    查完了要並啊!!!

  • y++;
    並完了邊數要++啊,不然建不出樹啊

  • if(f[i] == i)
    如果自己是自己的father那它就是根了,記得建虛根

  • for(register int i = 0; i <= n; i++) hs[i] = 10419
    因爲虛根是0,所以重兒子初值爲0一定會出錯的,要附一個大一點的值

總之歷盡千辛萬苦,最大生成樹被我寫出來了。
下面寫樹剖

樹剖大法好!!!

以前不會樹剖,總覺得是一個很高大上的東西,後來發現就是一個巧妙的暴力,通過兩遍dfs,得到一個dfs序,這樣樹上操作就變成了區間操作,隨便維護一下區間就好了。但這個dfs序有很多神奇的性質,能令鏈上操作的複雜度降至logN。
樹剖的核心就是
把樹剖成鏈後,每一個節點都有一個top值,記錄它所在的鏈的頂端是誰
這樣如果兩個點的top值一樣,那他們就在同一條鏈上,自然深度淺的節點就是他們的LCA。
如果兩個點不在同一條鏈上,即top值不同時,我們令top深的點往上跳,跳來跳去兩個點就在同一條鏈上了。
那回到這道題,我們要找兩個點間路徑的最小值。dx巧妙的把點權定義爲它到它父親的邊權,根的點權爲-1.這樣求邊權就成了求點權。而我沒有想到如此巧妙的做法,我暴力維護了邊權。我用一個cf數組記錄這個點到它父親的邊權,一個ct數組記錄它到top的最小邊權,dfs時把這兩個數組預處理出來,然後求LCA時就可以用ct數組直接查詢了。但兩個點的top要是一樣了,直接用ct查詢顯然是錯誤的,我就只能讓深的點往上跳,一邊跳一邊用cf來查詢。(如果樹退化成鏈並且每次都查詢鏈底到鏈頂的話就是N2算法了。。。還好數據沒卡)
最後是代碼

#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;

inline long long read()
{
    long long ans = 0, k = 1;
    char ch = getchar();
    while((ch < '0' || ch > '9') && ch != '-') ch = getchar();
    if(ch == '-') k = -1, ch = getchar();
    while(ch <= '9' && ch >= '0')
        ans = ans * 10 + ch - '0', ch = getchar();
    return ans * k;
}

inline void print(long long x)
{
    if(x < 0) { putchar('-'); print(-x); return ; }
    if(x > 9) print(x / 10);
    putchar(x % 10 + '0');
    return ;
}

int n, m, p;
struct edge
{
    int f, t, w;
};
vector <edge> e;
vector <edge> u[10420];
int f[10420];
int op[10420], sz[10420], hs[10420], dep[10420], top[10420], fa[10420], dfsx_p;
int cf[10420], ct[10420]; //costfather, costtop

inline int fnd(int x) { return x == f[x] ? x : fnd(f[x]); }

inline bool cmp(edge a, edge b) { return a.w > b.w; }

inline void dfs(int x)
{
    int len = u[x].size();
    sz[x]++;
    for(register int i = 0; i < len; i++)
        if(u[x][i].t != fa[x])
        {
            fa[u[x][i].t] = x;
            dep[u[x][i].t] = dep[x] + 1;
            dfs(u[x][i].t);
            sz[x] += sz[u[x][i].t];
            cf[u[x][i].t] = u[x][i].w;
            if(sz[u[x][i].t] > sz[hs[x]])
                hs[x] = u[x][i].t;
        }
    return ;
}

inline void DFS(int x, int r)
{
    if(x == r) ct[x] = 0x7fffffff;
    else       ct[x] = min(cf[x], ct[fa[x]]);
    top[x] = r;
    if(hs[x] != 10419) DFS(hs[x], r);
    int len = u[x].size();
    for(register int i = 0; i < len; i++)
        if(u[x][i].t != fa[x] && u[x][i].t != hs[x])
            DFS(u[x][i].t, u[x][i].t);
    return ;
}

inline void LCA(int x, int y)
{
    int ans = 0x7fffffff;
    while(top[x] != top[y])
    {
        if(dep[top[x]] > dep[top[y]]) swap(x, y);
        ans = min(ans, min(ct[y], cf[top[y]]));
        y = fa[top[y]];
    }
    if(dep[x] > dep[y]) swap(x, y);
    if(x == 0) {printf("-1\n"); return ; }
    while(dep[y] > dep[x])
    {
        ans = min(ans, cf[y]);
        y = fa[y];
    }
    printf("%d\n", ans);
    return ;
}

int main()
{
    freopen("truck.in", "r", stdin);
    freopen("truck.out", "w", stdout);
    n = read();
    m = read();
    register int x, y = 0;
    register edge s;
    for(register int i = 0; i <= n; i++)
        f[i] = i, hs[i] = 10419;
    for(register int i = 1; i <= m; i++)
    {
        s.f = read();
        s.t = read();
        s.w = read();
        e.push_back(s);
    }
    x = e.size();
    sort(e.begin(), e.end(), cmp);
    for(register int i = 0; i < x; i++)
    {
        int f1 = fnd(e[i].f), f2 = fnd(e[i].t);
        if(f1 != f2)
        {
            u[e[i].f].push_back(e[i]);
            swap(e[i].f, e[i].t);
            u[e[i].f].push_back(e[i]);
            y++;
            f[f1] = f2;
        }
        if(y == n - 1) break;
    }
    for(register int i = 1; i <= n; i++)
        if(f[i] == i)
        {
            s.f = 0;
            s.t = i;
            s.w = 0x7fffffff;
            u[0].push_back(s);
        }
    dfs(0);
    DFS(0, 0);
    p = read();
    for(register int i = 1; i <= p; i++)
    {
        x = read();
        y = read();
        LCA(x, y);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章