[WC2014]紫荆花之恋 - 动态点分治 + 平衡树(Part 2)

Description

维护一棵树,每次需要执行以下两个操作:

  1. 新增一个叶子节点
  2. 查询新增的叶子节点与原树上的多少个点满足两点的距离小于等于两点点权相加之和。

强制在线。

Solution

前置芝士

  1. 高速平衡树(指除了\(Fhq-Treap\)\(Splay\)以外的平衡树,或者你有高超的卡常技能也行)
  2. 动态点分治(替罪羊树式点分树)

前言

由于这道题涉及到的东西比较繁琐,所以接下来将分块讲解。

动态点分治

首先,如果这道题不强制在线的话要怎么做,我们观察一下题目给出的关于条件的式子:

\[dis_{i,j}\le r_i+r_j \]

考虑重心\(u\),那么式子就变成了

\[dis_{i,u}+dis_{j,u}\le r_i + r_j \]

移项

\[dis_{i,u}-r_i\le r_j - dis_{j,u} \]

所以如果不强制在线,应该就得到了一个比较显然的做法:建立点分树,对于每个点维护一棵平衡树,存储\(dis_{i,u}-r_i\),对于一个新加入的点\(x\),就在点分树上暴力跳祖先,并进行查询和更新。

然而,本题要求强制在线(说的就是你,毒瘤的强制在线!)。

平衡树

因为本题中所用到的平衡树功能比较单一,所以可以写高速平衡树。

博主查阅了部分博文,发现主要有以下几种平衡树:

  1. 替罪羊树,重构因子为\(0.86\)时的时间复杂度比较优秀

  2. \(Splay\)\(Fhq-Treap\),高超的卡常技巧

  3. \(SBT\)

  4. \(Treap\)

PS:当然,以上几种平衡树我并没有一一试验

替罪羊式点分树

前面说到离线的做法,既然强制在线,那我们就不能把点分树一次性全部建出来了。

那怎么办呢,每次加一个点就重构一次点分树不就行了。

考虑点分治时的分治中心,之所以要选在重心,是因为选在重心时效率最高,那么分治中心如果不在重心,显然也是可以的,所以每次新加入一个节点时,可以就将它加入到其在原树的父亲节点下面。

然后,为了保证复杂度,我们可以仿照替罪羊树的思想,设定一个重构因子\(\alpha\),当某个节点\(x\)的子树大小满足\(size_x>\alpha \cdot size_{fa_x}\)时,就暴力重构\(fa_x\)在点分树上的子树。

LCA

由于是动态加点,无法使用\(RMQ\)或树链剖分,只能使用倍增(倍增的胜利)。

时间复杂度及一些细节

本题的时间复杂度主要在外层点分树的重构,以及由于重构外层点分树内层平衡树的信息都必须重建上。

对于外层点分树,重构的时间复杂度每次均摊\(O(\log n)\)

对于内层平衡树的重建,因为每个平衡树平均\(\log n\)个节点,每次插入时间复杂度\(O(\log n)\),所以重建一棵平衡树时间复杂度\(O(\log^2 n)\)。一次内层重建\(\log n\)棵平衡树时间复杂度是\(O(\log n^3)\)

所以必须要使用高速平衡树,另外,由于一次重建的复杂度较高,经过不懈的尝试,我发现在我的程序里点分树的重建因子设成\(0.76\)时速度最快。

Code

#include <bits/stdc++.h>
using namespace std;

namespace IO {
const int maxn((1 << 21) + 1);
char ibuf[maxn], *iS, *iT, obuf[maxn], *oS = obuf, *oT = obuf + maxn - 1, ch,
                                       st[55];
int opt, tp;
char Getc() {
  return (iS == iT ? (iT = (iS = ibuf) + fread(ibuf, 1, maxn, stdin),
                      (iS == iT ? EOF : *iS++))
                   : *iS++);
}
void Flush() {
  fwrite(obuf, 1, oS - obuf, stdout);
  oS = obuf;
}
void Putc(char x) {
  *oS++ = x;
  if (oS == oT) Flush();
}
template <class Int>
void Input(Int& x) {
  for (opt = 1, ch = Getc(); ch < '0' || ch > '9'; ch = Getc())
    opt = ch == '-' ? -1 : 1;
  for (x = 0; ch <= '9' && ch >= '0'; ch = Getc())
    x = (x << 3) + (x << 1) + (ch ^ 48);
  x *= opt;
}
template <class Int>
void Print(Int x) {
  if (!x) Putc('0');
  if (x < 0) Putc('-'), x = -x;
  while (x) st[++tp] = x % 10 + '0', x /= 10;
  while (tp) Putc(st[tp--]);
}
}  // namespace IO
using IO::Flush;
using IO::Input;
using IO::Print;
using IO::Putc;

typedef long long ll;
const int _ = 1e5 + 10;
const int INF = 0x3f3f3f3f;
const int mod = 1e9;
int id, N, r[_];
int tot = 1, head[_], to[_ << 1], nxt[_ << 1], edge[_ << 1];

void adde(int x, int y, int z) {
  to[++tot] = y;
  edge[tot] = z;
  nxt[tot] = head[x];
  head[x] = tot;
}

namespace lca {
int dep[_], dis[_], fa[_][20];
void insert(int x, int y, int z) {
  adde(x, y, z);
  adde(y, x, z);
  dep[y] = dep[x] + 1;
  dis[y] = dis[x] + z;
  fa[y][0] = x;
  for (int i = 1; i <= 18; ++i) fa[y][i] = fa[fa[y][i - 1]][i - 1];
}
int query(int x, int y) {
  if (dep[x] < dep[y]) swap(x, y);
  for (int i = 18; i >= 0; --i)
    if (dep[fa[x][i]] >= dep[y]) x = fa[x][i];
  if (x == y) return x;
  for (int i = 18; i >= 0; --i)
    if (fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
  return fa[x][0];
}
int dist(int x, int y) { return dis[x] + dis[y] - 2 * dis[query(x, y)]; }
}  // namespace lca

struct Treap {
  int val, key, cnt, size;
  Treap *ls, *rs;
  static stack<Treap*> bin;
  void* operator new(size_t, int v) {
    Treap* ret;
    ret = bin.top();
    bin.pop();
    ret->val = v, ret->key = rand();
    ret->cnt = ret->size = 1;
    ret->ls = ret->rs = NULL;
    return ret;
  }
  void operator delete(void* t) { bin.push((Treap*)t); }
  void update() {
    size = cnt;
    if (ls) size += ls->size;
    if (rs) size += rs->size;
  }
  friend void zig(Treap*& p) {
    Treap* q = p->ls;
    p->ls = q->rs, q->rs = p;
    p->update(), q->update();
    p = q;
  }
  friend void zag(Treap*& p) {
    Treap* q = p->rs;
    p->rs = q->ls, q->ls = p;
    p->update(), q->update();
    p = q;
  }
  friend int query(Treap* p, int v) {
    if (p == NULL) return 0;
    if (v < p->val)
      return query(p->ls, v);
    else
      return p->cnt + (p->ls == NULL ? 0 : p->ls->size) + query(p->rs, v);
  }
  friend void insert(Treap*& p, int v) {
    if (p == NULL) {
      p = new (v) Treap;
      return;
    }
    if (v == p->val) {
      ++p->cnt;
      p->update();
      return;
    }
    if (v < p->val) {
      insert(p->ls, v);
      if (p->key < p->ls->key) zig(p);
    }
    if (v > p->val) {
      insert(p->rs, v);
      if (p->key < p->rs->key) zag(p);
    }
    p->update();
  }
  friend void remove(Treap*& p) {
    if (p == NULL) return;
    remove(p->ls);
    remove(p->rs);
    delete p;
    p = NULL;
  }
};
stack<Treap*> Treap::bin;
Treap node[_ << 7];

// 动态点分治
namespace Tree {
const double alpha = 0.76;
int fa[_], vis[_], vis2[_], tim;
int mxsiz, totsiz, root, siz[_];
Treap *dist[_], *sub[_];
set<int> son[_];
void remove(int x) {
  vis[x] = tim;
  remove(dist[x]);
  for (auto y : son[x]) {
    remove(y);
    remove(sub[y]);
  }
  son[x].clear();
}
void calcsiz(int x, int f) {
  siz[x] = 1;
  for (int i = head[x]; i; i = nxt[i]) {
    int y = to[i];
    if (vis[y] != tim || vis2[y] == tim || y == f) continue;
    calcsiz(y, x);
    siz[x] += siz[y];
  }
}
void getroot(int x, int f) {
  int maxx = 0;
  for (int i = head[x]; i; i = nxt[i]) {
    int y = to[i];
    if (vis[y] != tim || vis2[y] == tim || y == f) continue;
    getroot(y, x);
    maxx = max(maxx, siz[y]);
  }
  maxx = max(maxx, totsiz - siz[x]);
  if (maxx < mxsiz) mxsiz = maxx, root = x;
}
void reinsert(int x, int f, int dis, Treap*& p) {
  insert(p, dis - r[x]);
  for (int i = head[x]; i; i = nxt[i]) {
    int y = to[i], z = edge[i];
    if (y == f || vis[y] != tim || vis2[y] == tim) continue;
    reinsert(y, x, dis + z, p);
  }
}
int divide(int x) {
  calcsiz(x, 0);
  totsiz = siz[x], mxsiz = INF, root = 0;
  getroot(x, 0);
  vis2[x = root] = tim;
  reinsert(x, 0, 0, dist[x]);
  for (int i = head[x]; i; i = nxt[i]) {
    int y = to[i], z = edge[i];
    if (vis[y] != tim || vis2[y] == tim) continue;
    Treap* p = NULL;
    reinsert(y, 0, z, p);
    int rt = divide(y);
    fa[rt] = x, son[x].insert(rt);
    sub[rt] = p;
  }
  return x;
}
void rebuild(int x) {
  ++tim;
  remove(x);
  int f = fa[x];
  Treap* p = sub[x];
  sub[x] = NULL;
  if (f) son[f].erase(x);
  x = divide(x);
  fa[x] = f, sub[x] = p;
  if (f) son[f].insert(x);
}
ll insert(int x, int f) {
  fa[x] = f, son[f].insert(x);
  ll ans = 0;
  insert(dist[x], -r[x]);
  for (int i = x; fa[i]; i = fa[i]) {
    int d = lca::dist(x, fa[i]);
    insert(dist[fa[i]], d - r[x]);
    insert(sub[i], d - r[x]);
    ans += query(dist[fa[i]], r[x] - d);
    ans -= query(sub[i], r[x] - d);
  }
  int xx = 0;
  for (int i = x; fa[i]; i = fa[i])
    if (dist[i]->size > dist[fa[i]]->size * alpha) xx = fa[i];
  if (xx) rebuild(xx);
  return ans;
}
}  // namespace Tree

int main() {
#ifndef ONLINE_JUDGE
  freopen("flower.in", "r", stdin);
  freopen("flower.out", "w", stdout);
#endif
  srand(time(NULL));
  for (int i = 0; i < (_ << 7); ++i) Treap::bin.push(node + i);
  Input(id), Input(N);
  ll last = 0;
  for (int i = 1; i <= N; ++i) {
    int x, z, v;
    Input(x), Input(z), Input(v);
    x = x ^ (last % mod);
    lca::insert(x, i, z);
    r[i] = v;
    last += Tree::insert(i, x);
    Print(last), Putc('\n');
  }
  return Flush(), 0;
}
posted @ 2020-01-01 21:51  newbielyx  阅读(177)  评论(0编辑  收藏  举报