树剖Ⅰ-货车运输:树剖+最大生成树


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 
#include 
#include 
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  e;
vector  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;
}

你可能感兴趣的:(板子)