1. 程式人生 > >BZOJ4732 [清華集訓2016]資料互動(樹鏈剖分+線段樹+multiset)

BZOJ4732 [清華集訓2016]資料互動(樹鏈剖分+線段樹+multiset)

題目連結

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

題解

首先,一個比較顯然的結論是:對於一棵有根樹上的兩條鏈 \((x_1, y_1)\)\((x_2, y_2)\),若兩條鏈存在交點,必然有:\({\rm lca}_{x_1, y_1}\) 在鏈 \((x_2, y_2)\) 上,或者 \({\rm lca}_{x_2, y_2}\) 在鏈 \((x_1, y_1)\) 上。

這樣,我們可以令 \(a_u\) 表示「鏈的兩端點的 \(\rm lca\) 為點 \(u\) 」的鏈的權值和,\(b_u\) 表示「鏈經過點 \(u\)

但兩端點的 \(\rm lca\) 不為點 \(u\) 」的鏈的權值和。那麼:

  • 對於鏈的插入操作,插入權值為 \(w\) 的鏈 \((x, y)\) 時,我們只需使 \(a_{{\rm lca}_{x, y}}\) 增加 \(w\),鏈 \((x, y)\) 上除 \({\rm lca}_{x, y}\) 的所有點的 \(b\) 增加 \(w\)
  • 對於鏈的刪除操作,我們只需將鏈的權值改為 \(-w\) 即可,其餘操作同插入操作
  • 對於查詢操作,根據最開始給出的結論,對於一條路徑 \((x, y)\),所能得到的權值和即為路徑上所有結點的 \(a\) 數值之和再加上 \(b_{{\rm lca}_{x, y}}\)

首先我們考慮如何查詢答案。將原有根樹樹鏈剖分之後,任意一條路徑都可以分成三部分。令整條路徑深度最小的點為 \(u\),和 \(u\) 在同一條重鏈上且深度最大的點為 \(v\),那麼整條路徑可分為:由 \(u\) 的某個輕兒子引出的一條鏈(可以為空)+\(u\)\(v\) 的重鏈部分+由 \(v\) 的某個輕兒子引出的一條鏈(可以為空)。我們令 \(g_u\) 表示由點 \(u\) 的某個輕兒子引出的鏈的 \(\sum a\) 的最大值,那麼一條路徑的答案即為:\(g_u + g_v + b_u + w_{u, v}\)(注意這是 \(u \neq 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\) 只會對區間的左端點有影響,因此和普通的區間加操作一樣,線上段樹上記錄區間加標記再下傳即可。注意順便修改答案的 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 / 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);
  // jump lca
  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\n", *ans.rbegin());
  }
  return 0;
}