动态维护树的直径 2019ACM-ICPC上海网络预选赛 A题 Lightning Routing

2019ACM-ICPC上海网络预选赛 A题 Lightning Routing I 动态维护树的直径

      • 题意及题解
      • 线段树维护树的直径
      • 动态点分治

题意及题解

动态维护树的直径 2019ACM-ICPC上海网络预选赛 A题 Lightning Routing_第1张图片

类似题目:CEOI2019 / CodeForces 1192B. Dynamic Diameter

线段树维护树的直径

动态维护树的直径 2019ACM-ICPC上海网络预选赛 A题 Lightning Routing_第2张图片

const int MXN = 4e5 + 7;
const int MXE = 4e5 + 7;
typedef pair<int, LL> piL;
int n, m;
int fid[MXN], lid[MXN], rid[MXN], inde;
int fa[MXN], weight[MXN];
LL dis[MXN];
std::vector<piL > mp[MXN];
struct node {
    int x, y;
    LL z;
}edge[MXE];
void dfs_pre(int u, int ba) {
    fid[u] = ++ inde;
    rid[inde] = u;
    for(auto V: mp[u]) {
        int v = V.fi;
        if(v == ba) continue;
        fa[v] = u;
        weight[v] = V.se;
        dis[v] = dis[u] + V.se;
        dfs_pre(v, u);
        rid[++inde] = u;
    }
    lid[u] = inde;
}
struct lp {
    LL val, lcav, LM, MR, LMR, lazy;
    /*
    len(a, b) = dis[a] + dis[b] - 2 * Min([a <= c <= b] dis[c])
    线段树维护最大的len(a,b)即为直径。但是必须保证[a <= c <= b]
    val = dis[x], lcav = -2 * dis[x]
    LMR为直径
    LM = val + lcav
    MR = lcav + val
    LMR = big(LM + val, val + MR)
    */
    int p1, p2, p3, a, b;
    friend lp operator + (const lp&lson, const lp&rson) {
        lp rt;
        rt.p1 = (lson.val >= rson.val ? lson.p1 : rson.p1);
        rt.val = big(lson.val, rson.val);
        rt.lcav = big(lson.lcav, rson.lcav);
        if(lson.LM > rson.LM) rt.LM = lson.LM, rt.p2 = lson.p2;
        else rt.LM = rson.LM, rt.p2 = rson.p2;
        if(lson.MR > rson.MR) rt.MR = lson.MR, rt.p3 = lson.p3;
        else rt.MR = rson.MR, rt.p3 = rson.p3;
        if(lson.LMR > rson.LMR) rt.LMR = lson.LMR, rt.a = lson.a, rt.b = lson.b;
        else rt.LMR = rson.LMR, rt.a = rson.a, rt.b = rson.b;
        if(lson.val + rson.lcav > rt.LM) {
            rt.LM = lson.val + rson.lcav;
            rt.p2 = lson.p1;
        }
        if(lson.lcav + rson.val > rt.MR) {
            rt.MR = lson.lcav + rson.val;
            rt.p3 = rson.p1;
        }
        if(lson.LM + rson.val > rt.LMR) {
            rt.LMR = lson.LM + rson.val;
            rt.a = lson.p2, rt.b = rson.p1;
        }
        if(lson.val + rson.MR > rt.LMR) {
            rt.LMR = lson.val + rson.MR;
            rt.a = lson.p1, rt.b = rson.p3;
        }
        return rt;
    }
}cw[MXN<<2];
#define lson rt << 1
#define rson rt << 1 | 1
void push_up(int rt) { cw[rt] = cw[lson] + cw[rson]; }
void build(int l, int r, int rt) {
    if(l == r) {
        int x = rid[l];
        cw[rt].lazy = 0;
        cw[rt].val = dis[x];
        cw[rt].lcav = - (dis[x] + dis[x]);
        cw[rt].LM = cw[rt].MR = - dis[x];
        cw[rt].LMR = 0;
        cw[rt].p1 = cw[rt].p2 = cw[rt].p3 = cw[rt].a = cw[rt].b = x;
        return ;
    }
    int mid = (l + r) >> 1;
    build(l, mid, rt << 1), build(mid + 1, r, rt << 1 | 1);
    push_up(rt);
}
void push_down(int rt) {
    if(cw[rt].lazy == 0) return ;
    cw[lson].val += cw[rt].lazy;
    cw[rson].val += cw[rt].lazy;
    cw[lson].lcav -= cw[rt].lazy + cw[rt].lazy;
    cw[rson].lcav -= cw[rt].lazy + cw[rt].lazy;
    cw[lson].LM -= cw[rt].lazy;
    cw[rson].LM -= cw[rt].lazy;
    cw[lson].MR -= cw[rt].lazy;
    cw[rson].MR -= cw[rt].lazy;
    cw[lson].lazy += cw[rt].lazy;
    cw[rson].lazy += cw[rt].lazy;
    cw[rt].lazy = 0;
}
void update(int L, int R, LL v, int l, int r, int rt) {
    if(L <= l && r <= R) {
        cw[rt].val += v;
        cw[rt].lcav -= v + v;
        cw[rt].LM -= v;
        cw[rt].MR -= v;
        cw[rt].lazy += v;
        return ;
    }
    int mid = (l + r) >> 1;
    push_down(rt);
    if(L > mid) update(L, R, v, mid + 1, r, rson);
    else if(R <= mid) update(L, R, v, l, mid, lson);
    else {
        update(L, mid, v, l, mid, lson), update(mid + 1, R, v, mid + 1, r, rson);
    }
    push_up(rt);
}
LL query(int p, int l, int r, int rt) {
    if(l == r) return cw[rt].val;
    int mid = (l + r) >> 1;
    push_down(rt);
    if(p <= mid) return query(p, l, mid, lson);
    else return query(p, mid + 1, r, rson);
}
#undef lson
#undef rson
namespace LCA {
    int up[MXN][22], lens[MXN];
    int cnt, dfn[MXN], en[MXN], LOG[MXN];
    void dfs(int u, int ba) {
        lens[u] = lens[ba] + 1;
        dfn[++cnt] = u;
        en[u] = cnt;
        for(auto V: mp[u]) {
            int v = V.fi;
            if(v == ba) continue;
            dfs(v, u);
            dfn[++ cnt] = u;
        }
    }
    inline int cmp(int u, int v) { return lens[u] < lens[v] ? u: v; }
    void init() {
        cnt = lens[0] = 0;
        dfs(1, 0);
        LOG[1] = 0;
        for(int i = 2; i <= cnt; ++i) LOG[i] = LOG[i-1] + ((1<<(LOG[i-1]+1))==i);
        for(int i = 1; i <= cnt; ++i) up[i][0] = dfn[i];
        for(int j = 1; (1<<j) <= cnt; ++j)
            for(int i = 1; i + (1<<j) -1 <= cnt; ++i)
                up[i][j] = cmp(up[i][j-1], up[i+(1<<(j-1))][j-1]);
    }
    inline int lca(int x, int y) {
        int l = en[x], r = en[y];
        if(l > r) swap(l, r);
        int k = LOG[r - l + 1];
        return cmp(up[l][k], up[r-(1<<k)+1][k]);
    }
}
LL Get(int a, int b, int c) {
    return query(fid[a], 1, inde, 1) + query(fid[b], 1, inde, 1) - 2 * query(fid[c], 1, inde, 1);
}
int main() {
#ifndef ONLINE_JUDGE
    freopen("E://ADpan//in.in", "r", stdin);
    // freopen("E://ADpan//out.out", "w", stdout);
#endif
    n = read();
    for(int i = 1, a, b; i < n; ++i) {
        LL c;
        a = read(), b = read(), c = read();
        mp[a].eb(mk(b, c)), mp[b].eb(mk(a, c));
        edge[i] = {a, b, c};
        assert(c <= 1000000000);
    }
    LCA::init();
    dfs_pre(1, 0);
    build(1, inde, 1);
    m = read();
    char opt[2];
    int x;
    LL y;
    while(m --) {
        scanf("%s", opt);
        x = read();
        if(opt[0] == 'C') {
            y = read();
            assert(y <= 1000000000);
            int px = (fa[edge[x].x] == edge[x].y ? edge[x].x: edge[x].y);
            update(fid[px], lid[px], y - edge[x].z, 1, inde, 1);
            edge[x].z = y;
            weight[px] = y;
        }else {
            int a = cw[1].a, b = cw[1].b;
            int la = LCA::lca(a, x), lb = LCA::lca(b, x);
            printf("%lld\n", big(Get(x, a, la), Get(x, b, lb)));
        }
    }
#ifndef ONLINE_JUDGE
    cout << "time cost:" << 1.0*clock()/CLOCKS_PER_SEC << "ms" << endl;
#endif
    return 0;
}

动态点分治

  • 动态点分治维护树的直径
  • 对每个局部重心所支配的子树建一个线段树,记录所有点到他的直接距离。
  • 因为每个局部重心支配的子树大小不同,所以这个线段树用vector动态开点比较好,不然内存吃不消。
  • 显然树的直径,一定会在某个重心维护的子树中且经过重心这个点。并且直径两点不在这个重心的同一个子树内。
  • 那就再用一个multiset维护每个局部重心的每个子树里距离重心最远点的距离。记录一下两条最长链的和及端点,即为可能的直径。
  • 最后一个mutiset统计所有重心内的相对直径就可以得到真正的直径了。
  • 因为点分树只有log(n)层,所以修改就从较高的点沿着点分树往上走,暴力修改即可。

  • !!!注意:每次修改只能修改这条边所影响的那些点。
  • !!!!注意:修改完后要重新维护一下局部重心的每个子树的最长链。记住是局部重心的每个子树的最长链。所以你要记录一下每个点属于那个子树比较好。
const int MXN = 1e5 + 7;
const int MXE = 1e5 + 7;
typedef pair<int, LL> piL;
int n, m;
int fa[MXN], vis[MXN], sz[MXN], points, hvy, hvysiz, inde, dis[MXN];
unordered_map<int, int> fid[MXN], lid[MXN], whichson[MXN], bel[MXN];
std::vector<piL > mp[MXN];
struct LP {
    LL len;
    int x, y;
    bool operator <(const LP&b) const {
        if(len != b.len) return len < b.len;
        if(x != b.x) return x < b.x;
        return y < b.y;
    }
};
multiset<LP> ans;
struct node {
    int x, y;
    LL z;
}edge[MXE];
namespace LCA {
    int up[MXN * 2][22], lens[MXN];
    int cnt, dfn[MXN * 2], en[MXN], LOG[MXN * 2], inde, fid[MXN], lid[MXN], to[MXN];
    LL bit[MXN], dep[MXN];
    void bit_add(int x, LL v) {
        while(x <= n) {
            bit[x] += v;
            x += lowbit(x);
        }
    }
    LL bit_qry(int x) {
        x = fid[x];
        LL res = 0;
        while(x > 0) {
            res += bit[x];
            x -= lowbit(x);
        }
        return res;
    }
    void dfs(int u, int ba) {
        lens[u] = lens[ba] + 1;
        dfn[++cnt] = u;
        en[u] = cnt;
        fid[u] = ++ inde;
        to[inde] = u;
        for(auto V: mp[u]) {
            int v = V.fi;
            if(v == ba) continue;
            dep[v] = dep[u] + V.se;
            dfs(v, u);
            dfn[++ cnt] = u;
        }
        lid[u] = inde;
    }
    inline int cmp(int u, int v) { return lens[u] < lens[v] ? u: v; }
    void init() {
        inde = cnt = lens[0] = 0;
        dfs(1, -1);
        dep[0] = to[0] = 0;
        for(int i = 1; i <= n; ++i) bit_add(i, dep[to[i]] - dep[to[i-1]]);
        LOG[1] = 0;
        for(int i = 2; i <= cnt; ++i) LOG[i] = LOG[i-1] + ((1<<(LOG[i-1]+1))==i);
        for(int i = 1; i <= cnt; ++i) up[i][0] = dfn[i];
        for(int j = 1; (1<<j) <= cnt; ++j)
            for(int i = 1; i + (1<<j) -1 <= cnt; ++i)
                up[i][j] = cmp(up[i][j-1], up[i+(1<<(j-1))][j-1]);
    }
    inline int lca(int x, int y) {
        int l = en[x], r = en[y];
        if(l > r) swap(l, r);
        int k = LOG[r - l + 1];
        return cmp(up[l][k], up[r-(1<<k)+1][k]);
    }
}
struct Seg {
    std::vector<LL> sum, lazy;
    std::vector<int> is;
    multiset<LP> ms;
    void build(int l, int r, int rt) {
        if(l == r) {
            is[rt] = l;
            return;
        }
        int mid = (l + r) >> 1;
        build(l, mid, rt << 1), build(mid + 1, r, rt << 1 | 1);
        push_up(rt);
    }
    void resize(int _n) {
        sum.clear(), lazy.clear(), is.clear();
        sum.shrink_to_fit(), lazy.shrink_to_fit(), is.shrink_to_fit();
        sum.resize(4 * _n + 1), lazy.resize(4 * _n + 1), is.resize(4 * _n + 1);
        build(1, _n, 1);
        // debug(4 * _n)
        // for(int i = 0; i <= 4 * _n; ++i) assert(sum[i] == 0 && lazy[i] == 0);
    }
    void push_down(int rt) {
        if(!lazy[rt]) return ;
        lazy[rt << 1] += lazy[rt];
        lazy[rt << 1 | 1] += lazy[rt];
        sum[rt << 1] += lazy[rt];
        sum[rt << 1 | 1] += lazy[rt];
        lazy[rt] = 0;
    }
    void push_up(int rt) {
        if(sum[rt << 1] > sum[rt << 1 | 1]) {
            sum[rt] = sum[rt << 1];
            is[rt] = is[rt << 1];
        }else {
            sum[rt] = sum[rt << 1 | 1];
            is[rt] = is[rt << 1 | 1];
        }
    }
    void update(int L, int R, LL v, int l, int r, int rt) {
        if(L <= l && r <= R) {
            sum[rt] += v;
            lazy[rt] += v;
            return ;
        }
        int mid = (l + r) >> 1;
        push_down(rt);
        if(L > mid) update(L, R, v, mid + 1, r, rt << 1 | 1);
        else if(R <= mid) update(L, R, v, l, mid, rt << 1);
        else {
            update(L, mid, v, l, mid, rt << 1), update(mid + 1, R, v, mid + 1, r, rt << 1 | 1);
        }
        push_up(rt);
    }
    piL query(int L, int R, int l, int r, int rt) {
        if(L <= l && r <= R) return mk(is[rt], sum[rt]);
        int mid = (l + r) >> 1;
        push_down(rt);
        if(L > mid) return query(L, R, mid + 1, r, rt << 1 | 1);
        else if(R <= mid) return query(L, R, l, mid, rt << 1);
        piL a = query(L, mid, l, mid, rt << 1);
        piL b = query(mid + 1, R, mid + 1, r, rt << 1 | 1);
        return a.se > b.se ? a: b;
    }
    LP getans() {
        if((int)ms.size() == 1) return *ms.begin();
        else {
            LP a;
            auto it = ms.end();
            -- it;
            a.len = (*it).len;
            a.x = (*it).y;
            -- it;
            a.len += (*it).len;
            a.y = (*it).y;
            return a;
        }
    }
}seg[MXN];
void dfs_pre(int u, int ba) {
    sz[u] = 1;
    int mm = 0;
    for(auto V: mp[u]) {
        int v = V.fi;
        if(v == ba || vis[v]) continue;
        fa[v] = u;
        dfs_pre(v, u);
        sz[u] += sz[v];
        mm = big(sz[v], mm);
    }
    mm = big(points - sz[u], mm);
    if(hvy == -1 || mm < hvysiz) hvy = u, hvysiz = mm;
}
void dfs_sz(int u, int ba) {sz[u] = 1;for(auto V: mp[u]) {int v = V.fi;if(v == ba || vis[v]) continue;dfs_sz(v, u);sz[u] += sz[v];}}
void build_tree(int u, int ba, LL lst, int rt, int x) {
    fid[rt][u] = ++ inde;
    whichson[rt][inde] = u;
    if(x != -1) bel[rt][u] = x;
    for(auto V: mp[u]) {
        if(vis[V.fi] || V.fi == ba) continue;
        build_tree(V.fi, u, V.se, rt, x == -1?V.fi:x);
    }
    lid[rt][u] = inde;
    // debug(fid[rt][u], lid[rt][u], sz[rt])
    if(lst) seg[rt].update(fid[rt][u], lid[rt][u], lst, 1, sz[rt], 1);
}
void build_segtree(int u) {
    inde = 0;
    dfs_sz(u, 0);
    seg[u].resize(sz[u]);
    build_tree(u, 0, 0, u, -1);
    for(auto V: mp[u]) {
        if(vis[V.fi]) continue;
        piL a = seg[u].query(fid[u][V.fi], lid[u][V.fi], 1, lid[u][u], 1);
        seg[u].ms.insert({a.se, u, whichson[u][a.fi]});
    }
    if((int)seg[u].ms.size()) ans.insert(seg[u].getans());
}
void dfs_fz(int st, int ba) {
    hvy = hvysiz = -1;
    dfs_pre(st, 0);
    int u = hvy;
    fa[u] = ba;
    vis[u] = 1;
    dis[u] = dis[ba] + 1;
    build_segtree(u);
    for(auto V: mp[u]) {
        if(vis[V.fi] || V.fi == ba) continue;
        points = sz[V.fi];
        dfs_fz(V.fi, u);
    }
}
int main() {
    n = read();
    for(int i = 1, a, b; i < n; ++i) {
        LL c;
        a = read(), b = read(), c = read();
        mp[a].eb(mk(b, c)), mp[b].eb(mk(a, c));
        edge[i] = {a, b, c};
    }
    LCA::init();
    points = n;
    dfs_fz(1, 0);
    m = read();
    char opt[2];
    int x;
    LL y;
    while(m --) {
        scanf("%s", opt);
        x = read();
        if(opt[0] == 'C') {
            y = read();
            LL ty = y;
            y -= edge[x].z;
            int px = edge[x].x, py = edge[x].y;
            if(dis[px] > dis[py]) swap(px, py);
            int tmp = px;
            piL res;
            LP lp1;
            // debug(tmp, px, py, y, edge[x].z)
            while(tmp) {
                // debug(tmp)
                if((int)seg[tmp].ms.size()) {
                    lp1 = seg[tmp].getans();
                    auto IT = ans.lower_bound(lp1);
                    ans.erase(IT);
                }
                int ret = fid[tmp][px] < fid[tmp][py] ? py : px;
                res = seg[tmp].query(fid[tmp][bel[tmp][ret]], lid[tmp][bel[tmp][ret]], 1, lid[tmp][tmp], 1);
                // debug(tmp, fid[tmp][ret], lid[tmp][ret], res.se, whichson[tmp][res.fi])
                auto it = seg[tmp].ms.lower_bound({res.se, tmp, whichson[tmp][res.fi]});
                // debug((*it).len, (*it).x, (*it).y)
                seg[tmp].ms.erase(it);
                seg[tmp].update(fid[tmp][ret], lid[tmp][ret], y, 1, lid[tmp][tmp], 1);
                res = seg[tmp].query(fid[tmp][bel[tmp][ret]], lid[tmp][bel[tmp][ret]], 1, lid[tmp][tmp], 1);
                // debug(tmp, fid[tmp][ret], lid[tmp][ret], res.se, whichson[tmp][res.fi])
                seg[tmp].ms.insert({res.se, tmp, whichson[tmp][res.fi]});
                if((int)seg[tmp].ms.size()) ans.insert(seg[tmp].getans());
                tmp = fa[tmp];
            }
            // debug(edge[x].z, ty, y)
            edge[x].z = ty;
            if(LCA::lens[px] > LCA::lens[py]) swap(px, py);
            LCA::bit_add(LCA::fid[py], y);
            LCA::bit_add(LCA::lid[py] + 1, -y);
        }else {
            auto it = ans.end();
            -- it;
            int a = (*it).x, b = (*it).y;
            int la = LCA::lca(a, x), lb = LCA::lca(b, x);
            printf("%lld\n", LCA::bit_qry(x) + big(LCA::bit_qry(a) - LCA::bit_qry(la) * 2, LCA::bit_qry(b) - LCA::bit_qry(lb) * 2));
        }
    }
    return 0;
}

你可能感兴趣的:(线段树,技巧-分治/CDQ分治)