zoukankan      html  css  js  c++  java
  • loj2553. 「CTSC2018」暴力写挂

    题意

    给定两棵树(T, T'),求

    [max_{(x, y)} dep_x + dep_y - (dep_{lca(x, y)} + dep'_{lca'(x, y)}) ]

    题解

    经过一些化简后就变成了求

    [max_{(x, y)} frac{1}{2} dep_x + frac{1}{2} dep_y + frac{1}{2} dis(x, y) - dep'_{lca'(x, y)} ]

    有两种主流做法。
    一种是边分+虚树+dp。
    这种还没实现过。
    另一种是边分+点分树合并。
    考虑对第一棵树进行边分,设(d_x)(x)到当前边分中心的距离,则求

    [max_{(x, y)} frac{1}{2} (dep_x + d_x) + frac{1}{2} (dep_y + d_x) - dep'_{lca'(x, y)} ]

    在一次边分中,前半部分是容易求得的。
    考虑固定后面部分,即枚举(x, y)在第二棵树上的lca。
    枚举了lca,要保证(x, y)在lca的子树中,怎么办呢?
    考虑用对于第二棵树的每一个点建一类棵动态开点线段树,每棵这种树维护在第一棵树中经过该点在不同的点分子树中的贡献(要记录在不同的点分中心的左右两侧的产生的贡献,贡献的形式就是(dep_x + d_x)),然后在第二棵树上从下往上合并即可,合并的时候对于两个点在同一棵点分子树的同一个部分(左/右)的贡献取max。并且左边和左边合并,右边和右边合并,左边只能和右边产生贡献,右边只能和左边产生贡献,就和线段树合并类似了。至于复杂度,大概就是根据边分树的优越结构的性质(完全二叉树),就很优秀就是了。
    大概是(mathcal O(n log n))的。

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    const int N = 750005;
    struct tr {
        int tot, lnk[N], nxt[N << 1], son[N << 1], w[N << 1];
        tr () {
            tot = 1;
            memset(lnk, 0, sizeof lnk);
            memset(nxt, 0, sizeof nxt);
        }
        void add (int x, int y, int z) {
            nxt[++tot] = lnk[x], lnk[x] = tot, son[tot] = y, w[tot] = z;
        }
        void adds (int x, int y, int z) {
            add(x, y, z), add(y, x, z);
        }
    } T0, T1, T2;
    
    int n, m, s, e, mn, tot; ll ans;
    int sz[N], *pos[N], rt[N], las[N], lc[N * 20], rc[N * 20];
    ll dep[N], mx[N * 20][2];
    bool ban[N << 1];
    void append (int x, int y, int z) {
        T1.adds(las[x], ++m, 0), las[x] = m;
        T1.adds(m, y, z);
    }
    void build (int x, int p, ll d) {
        dep[x] = d;
        for (int j = T0.lnk[x]; j; j = T0.nxt[j]) {
            if (T0.son[j] != p) {
                append(x, T0.son[j], T0.w[j]);
                build(T0.son[j], x, d + T0.w[j]);
            }
        }
    }
    void gete (int x, int p) {
        sz[x] = 1;
        for (int j = T1.lnk[x], v; j; j = T1.nxt[j]) {
            if (!ban[j >> 1] && T1.son[j] != p) {
                gete(T1.son[j], x);
                sz[x] += sz[T1.son[j]];
                v = max(sz[T1.son[j]], s - sz[T1.son[j]]);
                if (mn > v) {
                    e = j, mn = v;
                }
            }
        }
    }
    void dfs (int x, int p, ll d, bool f) {
        if (x <= n) {
            *pos[x] = ++tot;
            mx[tot][f] = d + dep[x];
            mx[tot][f ^ 1] = -1e18;
            pos[x] = (f ? &rc[tot] : &lc[tot]);
        }
        for (int j = T1.lnk[x]; j; j = T1.nxt[j]) {
            if (!ban[j >> 1] && T1.son[j] != p) {
               dfs(T1.son[j], x, d + T1.w[j], f);
            }
        }
    }
    void dac (int x, int curs) {
        if (curs > 1) {
            s = curs, e = -1, mn = 1e9;
            gete(x, 0);
            ban[e >> 1] = 1;
            int u = T1.son[e ^ 1], v = T1.son[e], su = s - sz[v], sv = sz[v];
            dfs(u, 0, 0, 0), dfs(v, 0, T1.w[e], 1);
            dac(u, su), dac(v, sv);
        }
    }
    int merge (int x, int y, ll d) {
        if (!x || !y) {
            return x | y;
        }
        ans = max(ans, (mx[x][0] + mx[y][1]) / 2 - d);
        ans = max(ans, (mx[y][0] + mx[x][1]) / 2 - d);
        mx[x][0] = max(mx[x][0], mx[y][0]);
        mx[x][1] = max(mx[x][1], mx[y][1]);
        lc[x] = merge(lc[x], lc[y], d);
        rc[x] = merge(rc[x], rc[y], d);
        return x;
    }
    void solve (int x, int p, ll d) {
        ans = max(ans, dep[x] - d);
        for (int j = T2.lnk[x]; j; j = T2.nxt[j]) {
            if (T2.son[j] != p) {
                solve(T2.son[j], x, d + T2.w[j]);
                rt[x] = merge(rt[x], rt[T2.son[j]], d);
            }
        }
    }
    int main () {
        scanf("%d", &n);
        for (int i = 1, x, y, z; i < n; ++i) {
            scanf("%d%d%d", &x, &y, &z);
            T0.adds(x, y, z);
        }
        for (int i = 1; i <= n; ++i) {
            las[i] = i, pos[i] = &rt[i];
        }
        m = n;
        build(1, 0, 0);
        dac(1, m);
        for (int i = 1, x, y, z; i < n; ++i) {
            scanf("%d%d%d", &x, &y, &z);
            T2.adds(x, y, z);
        }
        solve(1, 0, 0);
        printf("%lld
    ", ans);
        return 0;
    }
    
  • 相关阅读:
    ConcurrentHashMap使用示例
    vss的ss.ini丢失或损坏导致的vss无法登录错误
    Arcgis中用滚轮做放大缩小为什么和一般软件反向
    MapControl控件
    string截取字符串
    C# CheckedListBox控件用法总结(怎样得到多选的值)
    通信串口中报ObjectDisposedException错误时怎么解决
    C#串口SerialPort常用属性方法
    SerialPort.DataReceived 事件
    C#的串口编程
  • 原文地址:https://www.cnblogs.com/psimonw/p/11480989.html
Copyright © 2011-2022 走看看