CF1192B Dynamic Diameter 題解

JiaY19發表於2024-06-08

思路

靜態 \(\text{top tree}\) 板子題。

定義

我們使用簇來表示樹上的一個連通塊。

可以按照如下方式定義一個簇:

  1. 一個簇可以表示為三元組 \((u,v,E)\),其中 \(u,v\) 為樹的節點,稱為簇的界點,\(E\) 為一個邊的集合,表示該簇包含的邊,路徑 \((u,v)\) 稱作簇路徑。
  2. \(u,v\) 分別為上界點與下界點,上界點是一個簇內深度最小的點,下界點則是除去上界點外的界點。
  3. 界點可以通俗的理解為簇與簇的分界點。
  4. 對於樹的一條邊 \((u,v)\)\((u,v,{(u,v)})\) 是一個簇。
  5. 對於簇 \((u,v,E_1)\) 與簇 \((v,w,E_2)\)\(E_1\cap E_2=\varnothing\),那麼 \((u,w,E_1\cup E_2)\) 也是一個簇,這個操作稱為 \(\text{compress}\)
  6. 對於簇 \((x,v,E_1)\) 與簇 \((x,w,E_2)\)\(E_1\cap E_2=\varnothing\),那麼 \((x,w,E_1\cup E_2)\) (或者 \((x,v,E_1\cup E_2)\))也是一個簇,這個操作稱為 \(\text{rake}\)

經典圖:

一個感性理解是 \(\text{rake}\) 將簇與簇融合,\(\text{compress}\) 將簇與簇拼接。

這兩個操作可以幫助我們將整棵樹合併為一個簇。

  • 對於一度點,進行 \(\text{rake}\)
  • 對於二度點,進行 \(\text{compress}\)

收縮過程中,簇的合併結構形成了一個樹,我們把這個樹稱為 \(\text{top tree}\)

現在,這樣一個 \(\text{top tree}\) 它的樹高有可能是 \(O(n)\) 的。

更優的樹

考慮構造一個樹高更小的 \(\text{top tree}\)

容易想到的是全域性平衡二叉樹。

全域性平衡二叉樹提供了一個分治方案使整棵樹的全域性平衡二叉樹樹高為 \(O(\log n)\)

我們同樣可以把這個分治方案放在 \(\text{top tree}\) 上。

具體的,將樹進行重鏈剖分。

然後對於輕兒子,先把它們 \(\text{rake}\) 起來。

再對重鏈分治 \(\text{compress}\),找分割點時,我們按每個點合併完輕兒子後的簇的大小帶權找到中點。

這樣就可以建出一顆樹高為 \(O(\log n)\)\(\text{top tree}\) 了。

關於這道題

建出 \(\text{top tree}\) 後有什麼用呢。

我們想線段樹一樣的操作它

對於動態直徑。

我們把每一個簇,維護三個值。

簇內部的最長距離及到兩個界點的最長距離。

合併時我們對兩種操作分類討論拼接起來即可。

可以發現,維護直徑的方式與線段樹維護最大子段和的方式是比較類似的。

這種維護方法就可以支援一些簡單的修改,比如此題的修改邊權。

時間複雜度:\(O(n\log n)\)

Code

/*
  ! 如果沒有天賦,那就一直重複
  ! Created: 2024/06/05 19:58:38
*/
#include <bits/stdc++.h>
using namespace std;

#define int long long
#define fro(i, x, y) for (int i = (x); i <= (y); i++)
#define pre(i, x, y) for (int i = (x); i >= (y); i--)

const int N = 2e5 + 10;

int n, q, w, ct, last, head[N];
int sz[N], sn[N], fa[N], rt[N], dep[N];
struct Node {
  int u, v, w;
} d[N];
struct edge {
  int to, nxt;
} e[N << 1];
struct node {
  enum { UNIT, RAKE, COMP } type;
  int u, v, sz, ls, rs, fa, ln, mi, un, vn;
} t[N << 1];

inline void add(int x, int y) {
  e[++ct] = {y, head[x]}, head[x] = ct;
  e[++ct] = {x, head[y]}, head[y] = ct;
}
inline void dfs(int now, int fa) {
  sz[now] = 1, dep[now] = dep[fa] + 1;
  for (int i = head[now]; i; i = e[i].nxt) {
    int x = e[i].to;
    if (x == fa) continue;
    dfs(x, now);
    t[x] = {node::UNIT, now, x, 1};
    sz[now] += sz[x];
    if (sz[x] > sz[sn[now]]) sn[now] = x;
  }
}
inline void pup(int p) {
  int x = t[p].ls, y = t[p].rs;
  if (t[p].type == node::RAKE) {
    t[p].ln = t[x].ln;
    t[p].mi = max({t[x].mi, t[y].mi, t[x].un + t[y].un});
    t[p].un = max(t[x].un, t[y].un);
    t[p].vn = max(t[x].vn, t[x].ln + t[y].un);
  } else if (t[p].type == node::COMP) {
    t[p].ln = t[x].ln + t[y].ln;
    t[p].mi = max({t[x].mi, t[y].mi, t[x].vn + t[y].un});
    t[p].un = max(t[x].un, t[x].ln + t[y].un);
    t[p].vn = max(t[y].vn, t[y].ln + t[x].vn);
  }
}
inline auto rake(int x, int y) {
  assert(t[x].u == t[y].u);
  return t[++ct] = {node::RAKE, t[x].u, t[x].v, t[x].sz + t[y].sz, x, y}, t[x].fa = t[y].fa = ct, pup(ct), ct;
}
inline auto comp(int x, int y) {
  assert(t[x].v == t[y].u);
  return t[++ct] = {node::COMP, t[x].u, t[y].v, t[x].sz + t[y].sz, x, y}, t[x].fa = t[y].fa = ct, pup(ct), ct;
}
template<typename T, typename Func>
inline int build(T l, T r, Func f) {
  if (r == l) return 0;
  if (r == l + 1) return *l;
  int all = 0, sum = 0;
  for (auto it = l; it != r; it++) all += t[*it].sz;
  T mid = l + 1;
  for (auto it = l; it != r; it++) {
    sum += t[*it].sz;
    if (sum <= all / 2) mid = it + 1; else break;
  }
  return f(build(l, mid, f), build(mid, r, f));
}
inline void sol(int now, int ff, bool f) {
  fa[now] = ff;
  if (sn[now]) sol(sn[now], now, false);
  for (int i = head[now]; i; i = e[i].nxt)
    if (e[i].to != sn[now] && e[i].to != fa[now]) sol(e[i].to, now, true);
  if (f) {
    vector<int> s1;
    if (ff) s1.push_back(now);
    for (int i = sn[now]; i; i = sn[i]) {
      vector<int> s2{i};
      for (int j = head[fa[i]]; j; j = e[j].nxt)
        if (e[j].to != i && e[j].to != fa[fa[i]]) s2.push_back(rt[e[j].to]);
      s1.push_back(build(s2.begin(), s2.end(), rake));
    }
    rt[now] = build(s1.begin(), s1.end(), comp);
  }
}
inline void upd(int x, int w) {
  t[x].un = t[x].vn = t[x].mi = t[x].ln = w;
  while (t[x].fa) {
    pup(t[x].fa), x = t[x].fa;
  }
}

signed main() {
  ios::sync_with_stdio(0), cin.tie(0);
  cin >> n >> q >> w;
  fro(i, 1, n - 1) {
    cin >> d[i].u >> d[i].v >> d[i].w;
    add(d[i].u, d[i].v);
  }
  ct = n;
  dfs(1, 0);
  sol(1, 0, 1);
  fro(i, 1, n - 1) {
    if (dep[d[i].u] > dep[d[i].v])
      swap(d[i].u, d[i].v);
    upd(d[i].v, d[i].w);
  }
  fro(i, 1, q) {
    int x, y;
    cin >> x >> y;
    x = (x + last) % (n - 1) + 1;
    y = (y + last) % (w);
    d[x].w = y;
    upd(d[x].v, d[x].w);
    cout << (last = t[rt[1]].mi) << "\n";
  }
  return 0;
}

相關文章