zoukankan      html  css  js  c++  java
  • BZOJ4732. [清华集训2016]数据交互(树链剖分+线段树+multiset)

    题目链接

    https://www.lydsy.com/JudgeOnline/problem.php?id=4732

    题解

    首先,一个正确性比较显然的结论是:对于一棵有根树上的两条链 ((x_1, y_1))((x_2, y_2)),若两条链存在交点,必然有:({ m lca}_{x_1, y_1}) 在链 ((x_2, y_2)) 上,或者 ({ m lca}_{x_2, y_2}) 在链 ((x_1, y_1)) 上。

    这样,我们可以令 (a_u) 表示「链的两端点的 ( m lca) 为点 (u) 」的链的权值和,(b_u) 表示「链经过点 (u) 但两端点的 ( m lca) 不为点 (u) 」的链的权值和。那么:

    • 对于链的插入操作,插入权值为 (w) 的链 ((x, y)) 时,我们只需使 (a_{{ m lca}_{x, y}}) 增加 (w),链 ((x, y)) 上除 ({ m lca}_{x, y}) 的所有点的 (b) 增加 (w)
    • 对于链的删除操作,我们只需将链的权值改为 (-w) 即可,其余操作同插入操作
    • 对于查询操作,根据最开始给出的结论,对于一条路径 ((x, y)),所能得到的权值和即为路径上所有结点的 (a) 数值之和再加上 (b_{{ m lca}_{x, y}})

    为了方便,我们将树链剖分后结点 (u) 的所有子结点中,通过轻边与结点 (u) 相连的称为 (u) 的轻儿子。

    首先我们考虑如何查询答案。将原树树链剖分之后,任意一条路径都可以分成三部分。令整条路径深度最小的点为 (u),和 (u) 在同一条重链上且深度最大的点为 (v),那么整条路径可分为:由 (u) 的某个轻儿子引出的一条链(可以为空)+(u)(v) 的重链部分+由 (v) 的某个轻儿子引出的一条链(可以为空)。我们令 (g_u) 表示由点 (u) 的某个轻儿子引出的链的 (sum a) 的最大值,那么一条路径的答案即为:(g_u + g_v + b_u + w_{u, v})(注意这是 (u eq v) 时的情况,当 (u = v) 时取的是点 (u) 的所有轻儿子引出的链的 (sum a) 的最大值与次大值)。其中,(w_{u, v}) 表示点 (u) 到点 (v) 的路径上所有点的 (a) 数值之和。

    除去答案式子中的 (b_u),那么对于一条重链而言,这就是一个连续最大和的形式,只不过两端点还应该加上 (g_u)(g_v)。这样,我们就可以用线段树来维护区间连续最大和了,对于两端的 (g),我们在记录左右最值时处理一下即可。由于查询的是整棵树的最大权路径,我们可以用一个 multiset 来储存每条重链的连续最大和,每次查询时查询 multiset 内的最大值即可。

    接下来考虑链的插入操作(删除也可看做插入权值为负的链),根据 (a, b) 数组的定义,每次插入一条链后 (a) 只需进行单点修改。而 (b) 数组则为区间修改(链上修改),不过注意到答案式子 (g_u + g_v + b_u + w_{u, v}) 中仅有 (u) 一个结点使用了 (b) 数组,换句话说,修改 (b) 只会对区间的左端点有影响,因此和普通的区间加操作一样,在线段树上记录区间加标记再下传即可。注意随着链上 (a) 数组的修改,链到根所有轻重链交替处结点的 (g) 数组也要修改。同时还要顺便修改答案的 multiset。

    时间复杂度 (O(n log^2 n))

    代码

    #include<bits/stdc++.h>
    
    using namespace std;
    
    #define rg register
    
    typedef long long ll;
    
    template<typename T> inline bool checkMax(T& a, const T& b) {
      return a < b ? a = b, true : false;
    }
    
    const int N = 1e5 + 10;
    
    struct Edge {
      Edge* next;
      int to;
      Edge () {}
      Edge (Edge* next, int to): next(next), to(to) {}
    } *first[N], pool[N << 1], *pis = pool;
    
    inline void add(int u, int v) {
      first[u] = new (pis++) Edge (first[u], v);
      first[v] = new (pis++) Edge (first[v], u);
    }
    
    int n, m, size[N], pa[N], heavy[N], top[N], dfn[N], arc_dfn[N], dfn_cnt, end_p[N], dep[N];
    multiset<ll> ans, g[N];
    
    inline void dfs1(int u, int pa) {
      size[u] = 1;
      int v = 0;
      for (Edge* now = first[u]; now; now = now->next) {
        if (now->to ^ pa) {
          ::pa[now->to] = u;
          dep[now->to] = dep[u] + 1;
          dfs1(now->to, u);
          size[u] += size[now->to];
          if (checkMax(v, size[now->to])) {
            heavy[u] = now->to;
          }
        }
      }
    }
    
    inline void dfs2(int u, int t) {
      top[u] = t;
      dfn[u] = end_p[u] = ++dfn_cnt, arc_dfn[dfn_cnt] = u;
      if (heavy[u]) {
        dfs2(heavy[u], t);
        end_p[u] = end_p[heavy[u]];
      } else {
        ans.insert(0);
      }
      for (Edge* now = first[u]; now; now = now->next) {
        if (now->to ^ pa[u] && now->to ^ heavy[u]) {
          g[u].insert(0);
          dfs2(now->to, now->to);
        }
      }
    }
    
    #define lo (o<<1)
    #define ro (o<<1|1)
    
    struct State {
      ll lv, rv, maxv, sumv;
      State () {
        lv = rv = maxv = sumv = 0;
      }
      inline State operator + (const State& a) const {
        State res;
        res.lv = max(lv, sumv + a.lv);
        res.rv = max(a.rv, a.sumv + rv);
        res.sumv = sumv + a.sumv;
        res.maxv = max(max(maxv, a.maxv), rv + a.lv);
        return res;
      }
    } s[N << 2];
    
    ll a[N << 2], addv[N << 2];
    
    inline void add_tag(int o, ll v) {
      addv[o] += v;
      s[o].rv += v;
      s[o].maxv += v;
    }
    
    inline void push_down(int o) {
      if (addv[o]) {
        add_tag(lo, addv[o]);
        add_tag(ro, addv[o]);
        addv[o] = 0;
      }
    }
    
    inline void modify_s(int l, int r, int o, int p, ll v) {
      if (l == r) {
        int u = arc_dfn[l];
        a[o] += v, s[o].sumv = a[o];
        multiset<ll>:: iterator it = g[u].end();
        ll res = 0;
        if (g[u].size() >= 1) {
          res += *--it;
        }
        s[o].lv = a[o] + res;
        s[o].rv = a[o] + res + addv[o];
        if (g[u].size() >= 2) {
          res += *--it;
        }
        s[o].maxv = res + a[o] + addv[o];
      } else {
        int mid = l + r >> 1;
        push_down(o);
        (p <= mid) ? modify_s(l, mid, lo, p, v) : modify_s(mid + 1, r, ro, p, v);
        s[o] = s[lo] + s[ro];
      }
    }
    
    inline void modify_tag(int l, int r, int o, int ql, int qr, ll v) {
      if (ql <= l && r <= qr) {
        return add_tag(o, v);
      } else {
        int mid = l + r >> 1;
        push_down(o);
        if (ql <= mid) {
          modify_tag(l, mid, lo, ql, qr, v);
        } if (qr > mid) {
          modify_tag(mid + 1, r, ro, ql, qr, v);
        }
        s[o] = s[lo] + s[ro];
      }
    }
    
    inline State query(int l, int r, int o, int ql, int qr) {
      if (ql <= l && r <= qr) {
        return s[o];
      } else {
        int mid = l + r >> 1;
        push_down(o);
        if (qr <= mid) {
          return query(l, mid, lo, ql, qr);
        } else if (ql > mid) {
          return query(mid + 1, r, ro, ql, qr);
        } else {
          return query(l, mid, lo, ql, qr) + query(mid + 1, r, ro, ql, qr);
        }
      }
    }
    
    inline void jump(int u, ll tmp, int lca, int w) {
      for (; u; u = pa[top[u]]) {
        int l = lca ? max(dfn[lca] + 1, dfn[top[u]]) : n + 1, r = dfn[u];
        int p = pa[top[u]];
        if (p) {
          State x = query(1, n, 1, dfn[top[p]], end_p[p]);
          ans.erase(ans.lower_bound(x.maxv));
          g[p].erase(g[p].lower_bound(tmp));
          tmp = x.lv;
        }
        if (l <= r) {
          ans.erase(ans.lower_bound(query(1, n, 1, dfn[top[u]], end_p[u]).maxv));
          modify_tag(1, n, 1, l, r, w);
          ans.insert(query(1, n, 1, dfn[top[u]], end_p[u]).maxv);
        }
        if (p) {
          g[p].insert(query(1, n, 1, dfn[top[u]], end_p[u]).lv);
          modify_s(1, n, 1, dfn[p], 0);
          ans.insert(query(1, n, 1, dfn[top[p]], end_p[p]).maxv);
        }
      }
    }
    
    inline void modify(int u, int v, int w) {
      int old_u = u, old_v = v, lca;
      for (; top[u] ^ top[v]; u = pa[top[u]]) {
        if (dep[top[u]] < dep[top[v]]) {
          swap(u, v);
        }
      }
      lca = dep[u] <= dep[v] ? u : v;
      u = old_u, v = old_v;
      jump(u, query(1, n, 1, dfn[top[u]], end_p[u]).lv, lca, w);
      jump(v, query(1, n, 1, dfn[top[v]], end_p[v]).lv, lca, w);
      State x = query(1, n, 1, dfn[top[lca]], end_p[lca]);
      ans.erase(ans.lower_bound(x.maxv));
      modify_s(1, n, 1, dfn[lca], w);
      ans.insert(query(1, n, 1, dfn[top[lca]], end_p[lca]).maxv);
      jump(lca, x.lv, 0, 0);
    }
    
    int req_u[N], req_v[N], req_w[N];
    
    int main() {
      scanf("%d%d", &n, &m);
      for (rg int i = 1; i < n; ++i) {
        int u, v; scanf("%d%d", &u, &v);
        add(u, v);
      }
      dfs1(1, 0);
      dfs2(1, 1);
      for (rg int i = 1; i <= m; ++i) {
        char cmd[4];
        scanf("%s", cmd);
        if (*cmd == '+') {
          scanf("%d%d%d", &req_u[i], &req_v[i], &req_w[i]);
          modify(req_u[i], req_v[i], req_w[i]);
        } else {
          int tim; scanf("%d", &tim);
          modify(req_u[tim], req_v[tim], -req_w[tim]);
        }
        printf("%lld
    ", *ans.rbegin());
      }
      return 0;
    }
    
  • 相关阅读:
    JAVA编程心得-JAVA实现CRC-CCITT(XMODEM)算法
    自学PHP 环境搭建
    Postfix+Amavisd-new+Spamassassin+ClamAV整合安装
    安装Apache Felix OSGI Framework小记
    C#多线程
    使用maven进行测试设置断点调试的方法
    2016第33周四
    Spring配置文件头及xsd文件版本
    2016第33周二
    web中的重定向与转发
  • 原文地址:https://www.cnblogs.com/ImagineC/p/9998020.html
Copyright © 2011-2022 走看看