欧拉序动态维护树直径

https://zhuanlan.zhihu.com/p/84236967

https://www.cnblogs.com/TinyWong/p/11260601.html

一个月过去了,我还是没有学动态点分治...

欧拉序保存了每个节点进入和返回的情况,$n$ 个结点的树,欧拉序列长度为 $2n - 1$。

两个结点的LCA就是它们在欧拉序中出现的位置的区间中,深度最小的那个结点。

对于边权为正的树,上面定义中的深度也可以换成到根的距离,用dis[u]表示结点 $u$ 到根的距离。

那么两个节点的距离就可以表示成 $dis[u] + dis[v] - 2 * dis[lca]$

设两个结点在欧拉序中出现的位置分别为 $p$,$q$,不要求第一次出现。

那么也就是 $dis[dfn[p]] + dis[dfn[q]] - 2 * min(dis[x])$,$x \in [p, q]$

对于修改一个边权,就是区间修改。这没啥好说的。

对于查询直径,一棵树的直径就是 $$ max_{1 \leq a \leq c \leq b \leq 2n - 1}{dis[a] + dis[b] - 2 * dis[c]} $$。

这个东西就相当于三个区间的合并,可以变成两个区间合并。

一个数组val存储dis值,一个数组m存储-2*dis值,一个数组lm存储该区间里的max{val}+ max{m},其中val是左儿子的,m是右儿子的。一个数组mr存储该区间里的max{m}+max{val},其中m是左儿子的,val是右儿子的。

一个数组lmr存储区间的直径,等于max{lm[lp] + val[rp],val[lp] + mr[rp]}

记录直径端点就再开几个数组存一下就好了。

CEOI 2019 day 1 online mirror (unrated, IOI format) B - Dynamic Diameter

#include <bits/stdc++.h>
#define ll long long

namespace IO
{
    char buf[1 << 21], buf2[1 << 21], a[20], *p1 = buf, *p2 = buf, hh = ‘\n‘;
    int p, p3 = -1;
    void read() {}
    void print() {}
    inline int getc() {
        return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++;
    }
    inline void flush() {
        fwrite(buf2, 1, p3 + 1, stdout), p3 = -1;
    }
    template <typename T, typename... T2>
    inline void read(T &x, T2 &... oth) {
        T f = 1; x = 0;
        char ch = getc();
        while (!isdigit(ch)) { if (ch == ‘-‘) f = -1; ch = getc(); }
        while (isdigit(ch)) { x = x * 10 + ch - 48; ch = getc(); }
        x *= f;
        read(oth...);
    }
    template <typename T, typename... T2>
    inline void print(T x, T2... oth) {
        if (p3 > 1 << 20) flush();
        if (x < 0) buf2[++p3] = 45, x = -x;
        do {
            a[++p] = x % 10 + 48;
        } while (x /= 10);
        do {
            buf2[++p3] = a[p];
        } while (--p);
        buf2[++p3] = hh;
        print(oth...);
    }
} // using namespace IO
#define read IO::read
#define print IO::print

const int N = 2e5 + 7;

int dfn[N], tol, fa[N], in[N], out[N];
ll dis[N], weight[N];
#define pii pair<ll, int>
#define fi first
#define se second
std::vector<std::pii> G[N];

void dfs(int u, int pre) {
    dfn[in[u] = ++tol] = u;
    for (auto p: G[u]) {
        int v = p.se;
        if (v == pre) continue;
        fa[v] = u;
        dis[v] = dis[u] + p.fi;
        weight[v] = p.fi;
        dfs(v, u);
        dfn[++tol] = u;
    }
    out[u] = tol;
}

struct E {
    int u, v;
} e[N];

struct Seg {
    #define lp p << 1
    #define rp p << 1 | 1
    static const int NN = N * 4;
    ll val[NN], m[NN], lm[NN], mr[NN], lmr[NN], lazy[NN];
    void pushup(int p) {
        val[p] = std::max(val[lp], val[rp]);
        m[p] = std::max(m[lp], m[rp]);
        lm[p] = std::max(std::max(lm[lp], lm[rp]), val[lp] + m[rp]);
        mr[p] = std::max(std::max(mr[lp], mr[rp]), m[lp] + val[rp]);
        lmr[p] = std::max(std::max(lmr[lp], lmr[rp]), std::max(val[lp] + mr[rp], lm[lp] + val[rp]));
    }
    void build(int p, int l, int r) {
        if (l == r) {
            int index = dfn[l];
            val[p] = dis[index];
            lazy[p] = 0;
            m[p] = -2 * dis[index];
            lm[p] = mr[p] = -dis[index];
            lmr[p] = 0;
            return;
        }
        int mid = l + r >> 1;
        build(lp, l, mid);
        build(rp, mid + 1, r);
        pushup(p);
    }
    void add(int p, ll w) {
        val[p] += w;
        lazy[p] += w;
        lm[p] -= w;
        mr[p] -= w;
        m[p] -= 2 * w;
    }
    void pushdown(int p) {
        if (lazy[p]) {
            add(lp, lazy[p]);
            add(rp, lazy[p]);
            lazy[p] = 0;
        }
    }
    void update(int p, int l, int r, int x, int y, ll w) {
        if (x <= l && y >= r) {
            add(p, w);
            return;
        }
        pushdown(p);
        int mid = l + r >> 1;
        if (x <= mid) update(lp, l, mid, x, y, w);
        if (y > mid) update(rp, mid + 1, r, x, y, w);
        pushup(p);
    }
} seg;

int main() {
//    freopen("in.txt", "r", stdin);
    int n, q; ll mod;
    read(n, q, mod);
    for (int i = 1; i < n; i++) {
        int u, v; ll c;
        read(u, v, c);
        G[u].push_back(std::pii(c, v));
        G[v].push_back(std::pii(c, u));
        e[i] = {u, v};
    }
    dfs(1, 0);
    seg.build(1, 1, tol);
    //print(tol);
    ll ans = 0;
    while (q--) {
        int d; ll w;
        read(d, w);
        d = (d + ans) % (n - 1) + 1;
        w = (w + ans) % mod;
        int u = (fa[e[d].u] == e[d].v) ? e[d].u : e[d].v;
        seg.update(1, 1, tol, in[u], out[u], w - weight[u]);
        weight[u] = w;
        print(ans = seg.lmr[1]);
    }
    IO::flush();
    return 0;
}

The Preliminary Contest for ICPC Asia Shanghai 2019 A - Lightning Routing I

一个点到其他任意端点的最远距离肯定是直径的端点之一。那就查询一下当前结点和两个直径端点的距离,取个max就好了。

#include <bits/stdc++.h>

namespace IO
{
    char buf[1 << 21], buf2[1 << 21], a[20], *p1 = buf, *p2 = buf, hh = ‘\n‘;
    int p, p3 = -1;
    void read() {}
    template <typename T, typename... T2>
    inline void read(T &x, T2 &... oth) {
        T f = 1; x = 0;
        char ch = getchar();
        while (!isdigit(ch)) { if (ch == ‘-‘) f = -1; ch = getchar(); }
        while (isdigit(ch)) { x = x * 10 + ch - 48; ch = getchar(); }
        x *= f;
        read(oth...);
    }
} // using namespace IO
#define read IO::read

#define ll long long
#define pii pair<ll, int>
#define fi first
#define se second
const int N = 2e5 + 7;

template<class T>
inline T max(T a, T b) {
    return a > b ? a : b;
}

template<class T>
inline T min(T a, T b) {
    return a < b ? a : b;
}

struct Edge {
    int u, v;
} edge[N];

int n, dep[N], fa[N], dfn[N], tol, in[N], out[N];
int lg[N], st[N][24];
ll dis[N], weight[N];
std::vector<std::pii> G[N];

struct Seg {
    #define lp p << 1
    #define rp p << 1 | 1
    static const int SIZE = 4 * N;
    ll val[SIZE], m[SIZE], lm[SIZE], mr[SIZE], lmr[SIZE], lazy[SIZE];
    int val_node[SIZE], lm_node[SIZE], mr_node[SIZE], node1[SIZE], node2[SIZE];
    void pushup(int p) {
        val[p] = max(val[lp], val[rp]);
        if (val[p] == val[lp])
            val_node[p] = val_node[lp];
        else
            val_node[p] = val_node[rp];
        m[p] = max(m[lp], m[rp]);
        lm[p] = max(max(lm[lp], lm[rp]), val[lp] + m[rp]);
        if (lm[p] == lm[lp])
            lm_node[p] = lm_node[lp];
        else if (lm[p] == lm[rp])
            lm_node[p] = lm_node[rp];
        else
            lm_node[p] = val_node[lp];
        mr[p] = max(max(mr[lp], mr[rp]), m[lp] + val[rp]);
        if (mr[p] == mr[lp])
            mr_node[p] = mr_node[lp];
        else if (mr[p] == mr[rp])
            mr_node[p] = mr_node[rp];
        else
            mr_node[p] = val_node[rp];
        lmr[p] = max(max(lmr[lp], lmr[rp]), max(lm[lp] + val[rp], val[lp] + mr[rp]));
        if (lmr[p] == lmr[lp])
            node1[p] = node1[lp], node2[p] = node2[lp];
        else if (lmr[p] == lmr[rp])
            node1[p] = node1[rp], node2[p] = node2[rp];
        else if (lmr[p] == lm[lp] + val[rp])
            node1[p] = lm_node[lp], node2[p] = val_node[rp];
        else
            node1[p] = val_node[lp], node2[p] = mr_node[rp];
    }
    void build(int p, int l, int r) {
        if (l == r) {
            int u = dfn[l];
            val[p] = dis[u];
            lazy[p] = 0;
            m[p] = -2 * dis[u];
            lm[p] = mr[p] = -dis[u];
            lmr[p] = 0;
            val_node[p] = lm_node[p] = mr_node[p] = u;
            return;
        }
        int mid = l + r >> 1;
        build(lp, l, mid);
        build(rp, mid + 1, r);
        pushup(p);
    }
    void tag(int p, ll w) {
        val[p] += w;
        lazy[p] += w;
        lm[p] -= w; mr[p] -= w;
        m[p] -= 2 * w;
    }
    void pushdown(int p) {
        if (lazy[p]) {
            tag(lp, lazy[p]);
            tag(rp, lazy[p]);
            lazy[p] = 0;
        }
    }
    void update(int p, int l, int r, int x, int y, ll w) {
        if (x <= l && y >= r) {
            tag(p, w);
            return;
        }
        pushdown(p);
        int mid = l + r >> 1;
        if (x <= mid) update(lp, l, mid, x, y, w);
        if (y > mid) update(rp, mid + 1, r, x, y, w);
        pushup(p);
    }
    ll query(int p, int l, int r, int pos) {
        if (l == r) return val[p];
        pushdown(p);
        int mid = l + r >> 1;
        ll ans = 0;
        if (pos <= mid) ans = query(lp, l, mid, pos);
        else ans = query(rp, mid + 1, r, pos);
        pushup(p);
        return ans;
    }
} seg;

void dfs(int u, int pre = 0, int d = 0, ll w = 0) {
    dep[u] = d;
    fa[u] = pre;
    dis[u] = w;
    dfn[in[u] = ++tol] = u;
    for (auto p: G[u]) {
        int v = p.se; ll c = p.fi;
        if (v == pre) continue;
        weight[v] = c;
        dfs(v, u, d + 1, w + c);
        dfn[++tol] = u;
    }
    out[u] = tol;
}

void init() {
    lg[1] = 0;
    for (int i = 2, j = 0; i < N; i++)
        lg[i] = (i == (1 << (j + 1))) ? ++j : j;
    for (int i = 1; i <= tol; i++)
        st[i][0] = i;
    for (int j = 1; (1 << j) <= tol; j++)
        for (int i = 1; i + (1 << j) - 1 <= tol; i++)
            st[i][j] = dep[dfn[st[i][j - 1]]] < dep[dfn[st[i + (1 << (j - 1))][j - 1]]] ?
                                                st[i][j - 1] : st[i + (1 << (j - 1))][j - 1];
}

ll get(int u, int v) {
    int x = in[u], y = in[v];
    if (x > y) std::swap(x, y);
    int logg = lg[y - x + 1];
    int lca = (dep[dfn[st[x][logg]]] < dep[dfn[st[y - (1 << logg) + 1][logg]]]) ? st[x][logg] : st[y - (1 << logg) + 1][logg];
    return seg.query(1, 1, tol, x) + seg.query(1, 1, tol, y) - 2 * seg.query(1, 1, tol, lca);
}

int main() {
    //freopen("in.txt", "r", stdin);
    read(n);
    for (int i = 1; i < n; i++) {
        int u, v; ll w;
        read(u, v, w);
        G[u].push_back(std::pii(w, v));
        G[v].push_back(std::pii(w, u));
        edge[i] = {u, v};
    }
    dfs(1);
    init();
    seg.build(1, 1, tol);
    int q;
    read(q);
    char opt[5] = {};
    for (; q--; ) {
        scanf("%s", opt);
        if (opt[0] == ‘C‘) {
            int k; ll w;
            read(k, w);
            int u = (fa[edge[k].u] == edge[k].v) ? edge[k].u : edge[k].v;
            seg.update(1, 1, tol, in[u], out[u], w - weight[u]);
            weight[u] = w;
        } else {
            int u;
            read(u);
            int x = seg.node1[1], y = seg.node2[1];
            printf("%lld\n", max(get(x, u), get(y, u)));
        }
    }
    return 0;
}

看到有别的写法,维护区间直径,一个点的时候就是直径端点就是自己,两个区间合并的时候,新的直径肯定是一个区间的某个直径端点和另一个区间的某个直直径端点,就取个max合并就好了。

不过是2log的。

原文地址:https://www.cnblogs.com/Mrzdtz220/p/11664538.html

时间: 2024-10-11 05:33:46

欧拉序动态维护树直径的相关文章

lca 欧拉序+rmq(st) 欧拉序+rmq(线段树) 离线dfs

https://www.luogu.org/problemnew/show/P3379 1.欧拉序+rmq(st) 1 /* 2 在这里,对于一个数,选择最左边的 3 选择任意一个都可以,[left_index,right_index],深度都大于等于这个数的深度 4 */ 5 #include <cstdio> 6 #include <cstdlib> 7 #include <cmath> 8 #include <cstring> 9 #include &

DFS序-树链剖序-欧拉序

,二叉树是一颗线段树,树状数组,树上的每个维护节点负责维护一个区间信息,节点之间又包含和属于的关系.例如线段树: DFS序: 我们通过对每个节点设置两个量,in和out.从根节点开始DFS搜索,in为第一次搜索到时的时间戳,out为退出出栈时的时间戳. 可以得到,例如我们要查询以b为根节点我们只需要查询区间[2,5]:要查询以c为根节点子树的信息,我们可以查询区间[6,7]:查询a需要查询区间[1,8].在程序中,当要查询修改时,我们可以用线段树去维护,因为这些序列的性质和线段树太像了.我们要注

P3379 【模板】最近公共祖先(LCA)(欧拉序+rmq)

P3379 [模板]最近公共祖先(LCA) 用欧拉序$+rmq$维护的$lca$可以做到$O(nlogn)$预处理,$O(1)$查询 从这里剻个图 #include<iostream> #include<cstdio> #include<vector> using namespace std; int read(){ char c=getchar(); int x=0; while(c<'0'||c>'9') c=getchar(); while('0'&l

HDU 4836 The Query on the Tree lca || 欧拉序列 || 动态树

lca的做法还是很明显的,简单粗暴, 不过不是正解,如果树是长链就会跪,直接变成O(n).. 最后跑的也挺快,出题人还是挺阳光的.. 动态树的解法也是听别人说能ac的,估计就是放在splay上剖分一下,做法还是比较复杂的,,, 来一发lca: #include <stdio.h> #include <iostream> #include <algorithm> #include <sstream> #include <stdlib.h> #inc

【BZOJ 3772】精神污染 主席树+欧拉序

这道题的内存-------真·精神污染---.. 这道题的思路很明了,我们就是要找每一个路径包含了多少其他路径那么就是找,有多少路径的左右端点都在这条路径上,对于每一条路径,我们随便选定一个端点作为第一关键字,另一个作为第二关键字,于是就有了两维限制,按照主席树的一般思路,我们把建树顺序作为一维,然后在里面维护另一维,那么我们在外面限制第一关键字,就是在树上建主席树,查询减LCA,在里面的话我们把每个点作为第一关键字对应的第二关键字,放入主席树,而主席树维护的是欧拉序区间,所以我们每次查询只用查

[xyz模拟题]动态维护树的直径

专出神题的xyz. 支持删加边.修改点权.维护树的直径. LCT 需要额外记录子树信息.用一个堆维护. #include<cstdio> #include<cstring> #include<algorithm> #include<set> #include<vector> #include<queue> using namespace std; #define rep(i,x,y) for(i=x;i<=y;i++) #def

Underground Lab CodeForces - 782E (欧拉序)

大意:$n$结点,$m$条边无向图, 有$k$个人, 每个人最多走$\left\lceil\frac {2n}{k}\right\rceil$步, 求一种方案使得$k$个人走遍所有的点 $n$结点树的欧拉序长度为$2n-1$, 直接取$dfs$树的欧拉序即可 #include <iostream> #include <algorithm> #include <math.h> #include <cstdio> #include <vector>

HDU 2586(LCA欧拉序和st表)

什么是欧拉序,可以去这个大佬的博客(https://www.cnblogs.com/stxy-ferryman/p/7741970.html)巨详细 因为欧拉序中的两点之间,就是两点遍历的过程,所以只要找遍历过程中对应的最小的深度就行了,这里用st表存,first存第一个u出现的地方,用value存欧拉序,同时用depth存对应深度 模板 1 struct node{ 2 int v,next,dist; 3 }a[maxn<<1]; 4 int n,m,tot,len; 5 int st[m

BZOJ 4034 树上操作(树的欧拉序列+线段树)

刷个清新的数据结构题爽一爽? 题意: 有一棵点数为 N 的树,以点 1 为根,且树点有边权.然后有 M 个 操作,分为三种: 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a . 操作 3 :询问某个节点 x 到根的路径中所有点的点权和. 注意到操作3,询问x到根的路径之间点权和,容易发现这就是欧拉序列中的前缀和. 所以按照树的欧拉序列建线段树,然后操作1就变成单点修改,操作2,就变成了区间内某些点+a,某些点-a,也容易用tag标记