UOJ#347. WC2018通道(边分治)

Posted Cyhlnj

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了UOJ#347. WC2018通道(边分治)相关的知识,希望对你有一定的参考价值。

传送门
就是求两个点 \(a,b\) 使得 \(dis_1(a,b)+dis_2(a,b)+dis_3(a,b)\) 最大

step1

对第一棵树边分治
那么变成 \(d_1(a)+d_1(b)+dis_2(a,b)+dis_3(a,b)\) 最大
并且 \(a,b\) 属于边分开的不同的集合 \(S,T\)

边分治

对于一条边,算经过这条边的路径的答案
点分治不方便的就是同一棵子树的容斥,而边分治不用考虑
直接边分治显然菊花就卡掉了
所以我们要转二叉树
具体来说就是把一个点的儿子建一棵线段树,添加虚拟节点,点权为其父亲的点权,只有线段树的叶子节点有边权

step2

对第二棵树建虚树
建出 \(S|T\) 的虚树
变成 \(d_1(a)+d_2(a)+d_1(b)+d_2(b)+dis_3(a,b)-2d_2(lca_2(a,b))\)
\(a\in S,b\in T\)
枚举 \(lca\) 那么变成求一个类似于 \(w_1(a)+w_2(b)+dis_3(a,b)\) 的最大值

step3

第三棵树不需要什么,上式类似于一个求最长链的东西
而两棵子树合并之后的最长链的端点一定是原来两棵树的最长链的端点
所以只需要在第三棵树上求距离就好了,然后在第二棵树上 \(dp\) 维护

奉上 \(7.6kb\) 大常数代码

# include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int maxn(1e5 + 5);

int cnt1, cnt2, lef[maxn], rig[maxn];
ll ans, val[maxn];

namespace Tree1 {
    int n, first[maxn], cnt, deep[maxn], st[20][maxn << 1], lg[maxn << 1], dfn[maxn], idx;
    ll dis[maxn];

    struct Edge {
        int to, next;
        ll w;
    } edge[maxn << 1];

    inline void Add(int u, int v, ll w) {
        edge[cnt] = (Edge){v, first[u], w}, first[u] = cnt++;
        edge[cnt] = (Edge){u, first[v], w}, first[v] = cnt++;
    }

    inline int Min(int x, int y) {
        return deep[x] < deep[y] ? x : y;
    }

    void Dfs(int u, int ff) {
        int e, v;
        dfn[u] = ++idx, st[0][idx] = u;
        for (e = first[u]; ~e; e = edge[e].next)
            if ((v = edge[e].to) != ff) {
                dis[v] = dis[u] + edge[e].w;
                deep[v] = deep[u] + 1;
                Dfs(v, u), st[0][++idx] = u;
            }
    }

    inline int LCA(int u, int v) {
        if (u == v) return u;
        u = dfn[u], v = dfn[v];
        if (u > v) swap(u, v);
        int len;
        len = lg[v - u + 1];
        return Min(st[len][u], st[len][v - (1 << len) + 1]);
    }

    inline ll Dis(int u, int v) {
        if (!u || !v) return -1e18;
        int lca;
        lca = LCA(u, v);
        return val[u] + val[v] + dis[u] + dis[v] - (dis[lca] << 1);
    }

    void Init() {
        int u, v, i, j;
        ll w;
        memset(first, -1, sizeof(first));
        for (i = 1; i < n; ++i) scanf("%d%d%lld", &u, &v, &w), Add(u, v, w);
        Dfs(1, 0);
        for (i = 2; i <= idx; ++i) lg[i] = lg[i >> 1] + 1;
        for (j = 1; j <= lg[idx]; ++j)
            for (i = 1; i + (1 << j) - 1 <= idx; ++i)
                st[j][i] = Min(st[j - 1][i], st[j - 1][i + (1 << (j - 1))]);
    }
}

namespace Tree2 {
    int n, first[maxn], cnt, deep[maxn], st[20][maxn << 1], lg[maxn << 1], dfn[maxn], idx, dfx[maxn], pid;
    int head[maxn], que[maxn], len, size[maxn], sta[maxn], vis[maxn];
    ll dis[maxn];

    struct Edge {
        int to, next;
        ll w;
    } edge[maxn << 1], edg[maxn << 1];

    inline void Add1(int u, int v, ll w) {
        edge[cnt] = (Edge){v, first[u], w}, first[u] = cnt++;
        edge[cnt] = (Edge){u, first[v], w}, first[v] = cnt++;
    }

    inline void Add2(int u, int v) {
        edg[cnt] = (Edge){v, head[u]}, head[u] = cnt++;
    }

    inline int Min(int x, int y) {
        return deep[x] < deep[y] ? x : y;
    }

    void Dfs(int u, int ff) {
        int e, v;
        dfn[u] = ++idx, st[0][idx] = u, dfx[u] = ++pid, size[u] = 1;
        for (e = first[u]; ~e; e = edge[e].next)
            if ((v = edge[e].to) != ff) {
                dis[v] = dis[u] + edge[e].w;
                deep[v] = deep[u] + 1;
                Dfs(v, u), st[0][++idx] = u;
                size[u] += size[v];
            }
    }

    inline int LCA(int u, int v) {
        if (u == v) return u;
        u = dfn[u], v = dfn[v];
        if (u > v) swap(u, v);
        int len;
        len = lg[v - u + 1];
        return Min(st[len][u], st[len][v - (1 << len) + 1]);
    }

    struct Info {
        int u, v;
        ll dis;

        inline Info(int _u = 0, int _v = 0) {
            u = _u, v = _v, dis = Tree1 :: Dis(u, v);
        }

        inline bool operator <(Info b) const {
            return dis < b.dis;
        }
    } f1[maxn], f2[maxn];

    inline Info operator +(Info a, Info b) {
        return max(max(max(a, b), max(Info(a.u, b.u), Info(a.u, b.v))), max(Info(a.v, b.u), Info(a.v, b.v)));
    }

    inline Info Merge(Info a, Info b) {
        return max(max(Info(a.u, b.u), Info(a.u, b.v)), max(Info(a.v, b.u), Info(a.v, b.v)));
    }

    void Init() {
        int u, v, i, j;
        ll w;
        memset(first, -1, sizeof(first));
        for (i = 1; i < n; ++i) scanf("%d%d%lld", &u, &v, &w), Add1(u, v, w);
        Dfs(1, 0);
        for (i = 2; i <= idx; ++i) lg[i] = lg[i >> 1] + 1;
        for (j = 1; j <= lg[idx]; ++j)
            for (i = 1; i + (1 << j) - 1 <= idx; ++i)
                st[j][i] = Min(st[j - 1][i], st[j - 1][i + (1 << (j - 1))]);
    }

    inline int Cmp(int x, int y) {
        return dfx[x] < dfx[y];
    }

    void Calc(int u, ll add) {
        int e, v;
        f1[u] = f2[u] = Info();
        if (vis[u]) (vis[u] == 1) ? f1[u] = Info(u, u) : f2[u] = Info(u, u);
        for (e = head[u]; ~e; e = edg[e].next) {
            v = edg[e].to, Calc(v, add);
            ans = max(ans, max(Merge(f1[u], f2[v]), Merge(f1[v], f2[u])).dis + add - (dis[u] << 1));
            f1[u] = f1[u] + f1[v], f2[u] = f2[u] + f2[v];
        }
    }

    void Solve(ll add) {
        int i, t, tp;
        len = cnt = tp = 0;
        for (i = 1; i <= cnt1; ++i) que[++len] = lef[i], vis[lef[i]] = 1;
        for (i = 1; i <= cnt2; ++i) que[++len] = rig[i], vis[rig[i]] = 2;
        for (i = 1; i <= len; ++i) val[que[i]] += dis[que[i]];
        sort(que + 1, que + len + 1, Cmp), t = len;
        for (i = 1; i < t; ++i) que[++len] = LCA(que[i], que[i + 1]);
        sort(que + 1, que + len + 1, Cmp);
        len = unique(que + 1, que + len + 1) - que - 1;
        for (i = 1; i <= len; ++i) head[que[i]] = -1;
        for (i = 1; i <= len; ++i) {
            while (tp && dfx[sta[tp]] + size[sta[tp]] <= dfx[que[i]]) --tp;
            if (tp) Add2(sta[tp], que[i]);
            sta[++tp] = que[i];
        }
        Calc(sta[1], add);
        for (i = 1; i <= len; ++i) val[que[i]] = vis[que[i]] = 0;
    }
}

namespace Tree3 {
    int n, first[maxn << 2], cnt, head[maxn], tmp[maxn << 2], len, m;
    int size[maxn << 2], vis[maxn << 2], rte, sz, mn, st1[maxn], tp1, st2[maxn], tp2;
    ll dis[maxn << 2], fw[maxn << 2];

    struct Edge {
        int to, next;
        ll w;
    } edge[maxn * 8], edg[maxn << 1];

    inline void Add1(int u, int v, ll w) {
        edg[cnt] = (Edge){v, head[u], w}, head[u] = cnt++;
        edg[cnt] = (Edge){u, head[v], w}, head[v] = cnt++;
    }

    inline void Add2(int u, int v, ll w) {
        edge[cnt] = (Edge){v, first[u], w}, first[u] = cnt++;
        edge[cnt] = (Edge){u, first[v], w}, first[v] = cnt++;
    }

    int Build(int l, int r) {
        if (l > r) return 0;
        if (l == r) return tmp[l];
        int mid, ls, rs, cur;
        mid = (l + r) >> 1, cur = ++n;
        ls = Build(l, mid), rs = Build(mid + 1, r);
        if (ls) Add2(cur, ls, ls <= m ? fw[ls] : 0);
        if (rs) Add2(cur, rs, rs <= m ? fw[rs] : 0);
        return cur;
    }

    void Rebuild(int u, int ff) {
        int e, v, mid, ls, rs;
        len = 0;
        for (e = head[u]; ~e; e = edg[e].next)
            if ((v = edg[e].to) != ff) fw[v] = edg[e].w, tmp[++len] = v;
        mid = (len + 1) >> 1;
        ls = Build(1, mid), rs = Build(mid + 1, len);
        if (ls) Add2(u, ls, ls <= m ? fw[ls] : 0);
        if (rs) Add2(u, rs, rs <= m ? fw[rs] : 0);
        for (e = head[u]; ~e; e = edg[e].next)
            if ((v = edg[e].to) != ff) Rebuild(v, u);
    }

    void Getroot(int u, int ff, int fe) {
        int e, v;
        size[u] = 1;
        for (e = first[u]; ~e; e = edge[e].next)
            if ((v = edge[e].to) != ff && !vis[e]) Getroot(v, u, e), size[u] += size[v];
        if (abs(sz - size[u] - size[u]) < mn) rte = fe, mn = abs(sz - size[u] - size[u]);
    }

    void Dfs(int u, int ff, ll d) {
        int e, v;
        if (u <= m) st1[++tp1] = u;
        dis[u] = d;
        for (e = first[u]; ~e; e = edge[e].next)
            if ((v = edge[e].to) != ff && !vis[e]) Dfs(v, u, d + edge[e].w);
    }

    void Solve(int e) {
        if (e < 0) return;
        int tmp1, tmp2, i, u1, u2;
        u1 = edge[e].to, u2 = edge[e ^ 1].to;
        vis[e] = vis[e ^ 1] = 1, tp1 = tp2 = 0;
        Dfs(u1, u2, 0);
        while (tp1) st2[++tp2] = st1[tp1], --tp1;
        Dfs(u2, u1, 0);
        cnt1 = cnt2 = 0;
        for (i = 1; i <= tp1; ++i) val[st1[i]] = dis[st1[i]], lef[++cnt1] = st1[i];
        for (i = 1; i <= tp2; ++i) val[st2[i]] = dis[st2[i]], rig[++cnt2] = st2[i];
        Tree2 :: Solve(edge[e].w);
        tmp1 = size[u1] > size[u2] ? sz - size[u2] : size[u1];
        tmp2 = size[u2] > size[u1] ? sz - size[u1] : size[u2];
        sz = tmp1, mn = n + 1, rte = -1, Getroot(u1, 0, -1), Solve(rte);
        sz = tmp2, mn = n + 1, rte = -1, Getroot(u2, 0, -1), Solve(rte);
    }

    void Init() {
        int u, v, i;
        ll w;
        memset(first, -1, sizeof(first));
        memset(head, -1, sizeof(head));
        m = n;
        for (i = 1; i < n; ++i) scanf("%d%d%lld", &u, &v, &w), Add1(u, v, w);
        cnt = 0, Rebuild(1, 0);
    }

    void Calc() {
        sz = n, mn = n + 1, Getroot(1, 0, -1), Solve(rte);
    }
}

int main() {
    int n;
    scanf("%d", &n);
    Tree1 :: n = Tree2 :: n = Tree3 :: n = n;
    Tree1 :: Init(), Tree2 :: Init(), Tree3 :: Init();
    Tree3 :: Calc(), printf("%lld\n", ans);
    return 0;
}

以上是关于UOJ#347. WC2018通道(边分治)的主要内容,如果未能解决你的问题,请参考以下文章

uoj#400. CTSC2018暴力写挂(边分治)

UOJ#400. CTSC2018暴力写挂 边分治 线段树合并

[UOJ#348][WC2018]州区划分

UOJ#348. WC2018州区划分

[WC2010]重建计划(分数规划+点分治+单调队列)

uoj349WC2018即时战略