点分治与动态点分治略解

暑假的时候学的算法,太久不用就忘记了代码怎么写。

点分治

就大概的讲一下ba。

将一棵无根树转化为以重心为根的有根树,假设为\(p\),那么对于树上的路径,就可以分为两类:

  1. 经过根节点\(p\)
  2. 包含于\(p\)的某一棵子树内(不经过根节点)

由于重心的性质:

以重心为根,任意一棵子树的大小都不超过整棵树大小的一半。

所以保证了复杂度为\(O(N\log N)\)

对于第一种路径直接进行统计,对于第二种路径,则删除与重心相连的所有边,然后分治下去。

需要注意的是,代码里的solve函数应该根据实际情况进行编写,而不是直接套模板。

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

inline int ty() {
  char ch = getchar();
  int x = 0, f = 1;
  while (ch < '0' || ch > '9') {
    if (ch == '-') f = -1;
    ch = getchar();
  }
  while (ch >= '0' && ch <= '9') {
    x = x * 10 + ch - '0';
    ch = getchar();
  }
  return x * f;
}

const int _ = 1e5 + 10;
const int INF = 0x3f3f3f3f;
int tot, 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;
}

int N, K;
int root, totsiz, mxsiz, siz[_], vis[_], ans;
int dis[_], cnt;

void getroot(int x, int fa) {
  siz[x] = 1;
  int maxx = 0;
  for (int i = head[x]; i; i = nxt[i]) {
    int y = to[i];
    if (vis[y] || y == fa) continue;
    getroot(y, x);
    siz[x] += siz[y];
    maxx = max(maxx, siz[y]);
  }
  maxx = max(maxx, totsiz - siz[x]);
  if (maxx < mxsiz) mxsiz = maxx, root = x;
}

void query(int x, int fa, int d) {
  dis[++cnt] = d;
  for (int i = head[x]; i; i = nxt[i]) {
    int y = to[i], z = edge[i];
    if (vis[y] || y == fa) continue;
    query(y, x, d + z);
  }
}

int solve(int x, int s) {
  cnt = 0;
  query(x, 0, s);
  sort(dis + 1, dis + cnt + 1);
  int l = 1, r = cnt, sum = 0;
  while (l < r) {
    if (dis[l] + dis[r] <= K)
      sum += r - l, ++l;
    else
      --r;
  }
  return sum;
}

void divide(int x) {
  ans += solve(x, 0), vis[x] = 1;
  int nowsiz = totsiz;
  for (int i = head[x]; i; i = nxt[i]) {
    int y = to[i], z = edge[i];
    if (vis[y]) continue;
    ans -= solve(y, z);
    totsiz = siz[y] > siz[x] ? nowsiz - siz[x] : siz[y];
    mxsiz = INF, root = 0;
    getroot(y, 0);
    divide(root);
  }
}

int main() {
#ifndef ONLINE_JUDGE
  freopen("tree.in", "r", stdin);
  freopen("tree.out", "w", stdout);
#endif
  N = ty();
  for (int i = 1; i < N; ++i) {
    int x = ty(), y = ty(), z = ty();
    adde(x, y, z);
    adde(y, x, z);
  }
  K = ty();
  totsiz = N, mxsiz = INF;
  getroot(1, 0);
  divide(root);
  printf("%d\n", ans);
  return 0;
}

动态点分治

好了,这才是今天的主角。

点分树

建立点分树只需要在点分治的基础上,加一行代码fa[root]=x

void divide(int x) {
  vis[x] = 1;
  int nowsiz = totsiz;
  for (int i = head[x]; i; i = nxt[i]) {
    int y = to[i], z = edge[i];
    if (vis[y]) continue;
    mxsiz = INF, root = 0;
    totsiz = siz[y] > siz[x] ? nowsiz - siz[x] : siz[y];
    getroot(y, 0);
    fa[root] = x;
    divide(root);
  }
}

实现修改

修改的时候不断暴跳点分树即可。

是的,你没看错,这就是动态点分治

下面根据例题来具体解读

[ZJOI2007]捉迷藏

求出点分树,对于每个结点\(x\)维护两个 可删堆\(dist_x\)存储结点\(x\)代表的连通块中的所有黑点到\(fa_x\)的距离信息, \(sub_x\)表示结点\(x\)在点分树上的所有儿子和它自己中的黑点到\(x\)的距离信息,由于本题贪心的求答案方法,且两个来自于同一子树的路径不能成为一条完成的路径,我们只在这个堆中插入其自己的值和其每个子树中的最大值。我们发现,\(sub_x\)中最大的两个值的和就是分治时分支中心为\(x\)时经过结点\(x\)的最长黑端点路径。我们可以用全局可删堆\(ans\)存储所有结点的答案,这个堆中的最大值就是我们所求的答案。

#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--]);
}

void Getstr(char *s) {
  for (ch = Getc(); ch < 'A' || ch > 'Z'; ch = Getc())
    ;
  for (; ch <= 'Z' && ch >= 'A'; ch = Getc()) *s++ = ch;
  *s = 0;
}

void Putstr(const char *s) {
  for (int i = 0, n = strlen(s); i < n; ++i) Putc(s[i]);
}
}  // namespace IO
using IO::Flush;
using IO::Getstr;
using IO::Input;
using IO::Print;
using IO::Putc;

const int _ = 1e5 + 10;
const int INF = 0x3f3f3f3f;

int tot, head[_], to[_ << 1], nxt[_ << 1];
void adde(int x, int y) {
  to[++tot] = y;
  nxt[tot] = head[x];
  head[x] = tot;
}

struct Heap {
  priority_queue<int> data, del;
  void insert(int x) { data.push(x); }
  void erase(int x) { del.push(x); }
  void pop() {
    while (del.size() && data.top() == del.top()) data.pop(), del.pop();
    data.pop();
  }
  int top() {
    while (del.size() && data.top() == del.top()) data.pop(), del.pop();
    return data.top();
  }
  int top2() {
    int t = top(), ret;
    pop();
    ret = top();
    data.push(t);
    return ret;
  }
  int size() { return data.size() - del.size(); }
} dist[_], sub[_], ans;

int N, T;
int siz[_], vis[_], fa[_], dep[_], mxsiz, totsiz, root;

void getroot(int x, int f) {
  siz[x] = 1;
  int maxx = 0;
  for (int i = head[x]; i; i = nxt[i]) {
    int y = to[i];
    if (vis[y] || y == f) continue;
    getroot(y, x);
    siz[x] += siz[y];
    maxx = max(maxx, siz[y]);
  }
  maxx = max(maxx, totsiz - siz[x]);
  if (maxx < mxsiz) mxsiz = maxx, root = x;
}

void dfs(int x, int fa, int d, Heap &s) {
  s.insert(d);
  for (int i = head[x]; i; i = nxt[i]) {
    int y = to[i];
    if (vis[y] || y == fa) continue;
    dfs(y, x, d + 1, s);
  }
}

void divide(int x) {
  vis[x] = 1;
  int nowsiz = totsiz;
  for (int i = head[x]; i; i = nxt[i]) {
    int y = to[i];
    if (vis[y]) continue;
    mxsiz = INF, totsiz = siz[y] > siz[x] ? nowsiz - siz[x] : siz[y];
    getroot(y, 0);
    fa[root] = x;
    dfs(y, x, 1, dist[root]);
    sub[x].insert(dist[root].top());
    dep[root] = dep[x] + 1;
    divide(root);
  }
  sub[x].insert(0);
  if (sub[x].size() >= 2)
    ans.insert(sub[x].top() + sub[x].top2());
  else if (sub[x].size())
    ans.insert(sub[x].top());
}

namespace lca {
int top[_], dep[_], ssize[_], hson[_], fa[_];
void dfs1(int u, int ff) {
  fa[u] = ff;
  ssize[u] = 1;
  dep[u] = dep[ff] + 1;
  for (int i = head[u]; i; i = nxt[i]) {
    int v = to[i];
    if (v == ff) continue;
    dfs1(v, u);
    ssize[u] += ssize[v];
    if (ssize[hson[u]] < ssize[v]) hson[u] = v;
  }
}
void dfs2(int u, int tp) {
  top[u] = tp;
  if (hson[u]) dfs2(hson[u], tp);
  for (int i = head[u]; i; i = nxt[i]) {
    int v = to[i];
    if (v == fa[u] || v == hson[u]) continue;
    dfs2(v, v);
  }
}
void init() {
  dfs1(1, 0);
  dfs2(1, 1);
}
int query(int u, int v) {
  while (top[u] != top[v]) {
    if (dep[top[u]] < dep[top[v]]) swap(u, v);
    u = fa[top[u]];
  }
  return dep[u] < dep[v] ? u : v;
}
int dis(int u, int v) { return dep[u] + dep[v] - dep[query(u, v)] * 2; }
}  // namespace lca

int light[_], d[_][20];

inline void turn_off(int x) {
  if (sub[x].size() >= 2) ans.erase(sub[x].top() + sub[x].top2());
  sub[x].insert(0);
  if (sub[x].size() >= 2) ans.insert(sub[x].top() + sub[x].top2());
  for (int i = x; fa[i]; i = fa[i]) {
    if (sub[fa[i]].size() >= 2) ans.erase(sub[fa[i]].top() + sub[fa[i]].top2());
    if (dist[i].size()) sub[fa[i]].erase(dist[i].top());
    dist[i].insert(d[x][dep[x] - dep[fa[i]]]);
    sub[fa[i]].insert(dist[i].top());
    if (sub[fa[i]].size() >= 2)
      ans.insert(sub[fa[i]].top() + sub[fa[i]].top2());
  }
}

inline void turn_on(int x) {
  if (sub[x].size() >= 2) ans.erase(sub[x].top() + sub[x].top2());
  sub[x].erase(0);
  if (sub[x].size() >= 2) ans.insert(sub[x].top() + sub[x].top2());
  for (int i = x; fa[i]; i = fa[i]) {
    if (sub[fa[i]].size() >= 2) ans.erase(sub[fa[i]].top() + sub[fa[i]].top2());
    sub[fa[i]].erase(dist[i].top());
    dist[i].erase(d[x][dep[x] - dep[fa[i]]]);
    if (dist[i].size()) sub[fa[i]].insert(dist[i].top());
    if (sub[fa[i]].size() >= 2)
      ans.insert(sub[fa[i]].top() + sub[fa[i]].top2());
  }
}

int main() {
#ifndef ONLINE_JUDGE
  freopen("hide.in", "r", stdin);
  freopen("hide.out", "w", stdout);
#endif
  Input(N);
  for (int i = 1; i < N; ++i) {
    int x, y;
    Input(x), Input(y);
    adde(x, y);
    adde(y, x);
  }
  lca::init();

  mxsiz = INF, totsiz = N;
  getroot(1, 0);
  divide(root);
  // for (int i = 1; i <= N; ++i) printf("%d %d\n", i, fa[i]);
  for (int i = 1; i <= N; ++i)
    for (int j = i; j; j = fa[j]) d[i][dep[i] - dep[j]] = lca::dis(i, j);

  Input(T);
  while (T--) {
    char op[3];
    Getstr(op);
    if (op[0] == 'G') {
      if (ans.size())
        Print(ans.top()), Putc('\n');
      else
        Print(-1), Putc('\n');
    } else {
      int x;
      Input(x);
      if (light[x])
        turn_off(x);
      else
        turn_on(x);
      light[x] ^= 1;
    }
  }
  return Flush(), 0;
}

BZOJ3730 震波

Description

给你一棵树,每个节点有点权,你需要执行以下两种操作:

  1. 查询与点\(x\)距离不超过\(k\)的所有点的点权和
  2. 将点\(x\)的点权修改为\(y\)

强制在线。

Solution

还是仿照上一题的思路,将点分树建出来,然后对于每个节点,建立两个树状数组\(dist_x,sub_x\),下标为距离,权值为点权和。其中,\(dist_x\)表示\(x\)的联通块内,所有点到\(x\)的距离信息;\(sub_x\)表示\(x\)的联通块内,所有点到\(x\)在点分树上的父亲的距离信息。

对于查询,不断暴跳父亲,然后容斥一下即可。

对于修改,同样也是不断暴跳父亲,然后维护\(dist_x,sub_x\)

需要注意的是在向上跳暴力修改的过程中,要判断节点\(x\)到当前节点的距离是否已经大于\(y-len\),且不合法时不是break而应该是continue,就这个我一开始用线段树写没判居然能过,改成树状数组后调了我好久。。。
PS:不过这次调试过程中,我发现了又一法宝:assert,用这东西两下子就找出来了哪里出问题了!

Code

动态开点线段树(TLE)

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

bool xxxxx;

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;

const int _ = 1e5 + 10;
const int __ = 5e6 + 10;
const int INF = 0x3f3f3f3f;

int tot, head[_], to[_ << 1], nxt[_ << 1];

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

int N, M, val[_];

namespace lca {
int fa[_], son[_], dep[_], siz[_], top[_];
void dfs1(int x, int f) {
  siz[x] = 1;
  int maxx = 0;
  for (int i = head[x]; i; i = nxt[i]) {
    int y = to[i];
    if (y == f) continue;
    fa[y] = x, dep[y] = dep[x] + 1;
    dfs1(y, x);
    siz[x] += siz[y];
    if (siz[y] > maxx) maxx = siz[y], son[x] = y;
  }
}
void dfs2(int x, int topf) {
  top[x] = topf;
  if (!son[x]) return;
  dfs2(son[x], topf);
  for (int i = head[x]; i; i = nxt[i]) {
    int y = to[i];
    if (y == fa[x] || y == son[x]) continue;
    dfs2(y, y);
  }
}
void init() {
  dfs1(1, 0);
  dfs2(1, 1);
}
int query(int x, int y) {
  while (top[x] != top[y]) {
    if (dep[top[x]] < dep[top[y]]) swap(x, y);
    x = fa[top[x]];
  }
  return dep[x] < dep[y] ? x : y;
}
int dis(int x, int y) { return dep[x] + dep[y] - 2 * dep[query(x, y)]; }
}  // namespace lca

struct SegmentTree {
  int cnt, rt[_], ls[__], rs[__], sum[__];
  void modify(int &p, int l, int r, int x, int v) {
    if (!p) p = ++cnt;
    if (l == r) {
      sum[p] += v;
      return;
    }
    int mid = (l + r) >> 1;
    if (x <= mid)
      modify(ls[p], l, mid, x, v);
    else
      modify(rs[p], mid + 1, r, x, v);
    sum[p] = sum[ls[p]] + sum[rs[p]];
  }
  int query(int &p, int l, int r, int x, int y) {
    if (!p) return 0;
    if (x <= l && r <= y) return sum[p];
    int mid = (l + r) >> 1;
    if (y <= mid)
      return query(ls[p], l, mid, x, y);
    else if (x > mid)
      return query(rs[p], mid + 1, r, x, y);
    else
      return query(ls[p], l, mid, x, mid) +
             query(rs[p], mid + 1, r, mid + 1, y);
  }
} dist, sub;

int mxsiz, totsiz, root;
int vis[_], siz[_], fa[_], dep[_];  //, d[_][20];

void getroot(int x, int f) {
  siz[x] = 1;
  int maxx = 0;
  for (int i = head[x]; i; i = nxt[i]) {
    int y = to[i];
    if (vis[y] || y == f) continue;
    getroot(y, x);
    siz[x] += siz[y];
    maxx = max(maxx, siz[y]);
  }
  maxx = max(maxx, totsiz - siz[x]);
  if (maxx < mxsiz) mxsiz = maxx, root = x;
}

// 将距离信息插入到线段树dist中
void dfsdist(int x, int f, int d, const int s) {
  dist.modify(dist.rt[s], 0, N, d, val[x]);
  for (int i = head[x]; i; i = nxt[i]) {
    int y = to[i];
    if (vis[y] || y == f) continue;
    dfsdist(y, x, d + 1, s);
  }
}

// 将距离信息插入到线段树sub中
void dfssub(int x, int f, int d, const int s) {
  // printf("%d %d %d %d\n", x, d, val[x], s);
  sub.modify(sub.rt[s], 0, N, d, val[x]);
  for (int i = head[x]; i; i = nxt[i]) {
    int y = to[i];
    if (vis[y] || y == f) continue;
    dfssub(y, x, d + 1, s);
  }
}

// 建立点分树
void divide(int x) {
  vis[x] = 1;
  dfsdist(x, 0, 0, x);
  int nowsiz = totsiz;
  for (int i = head[x]; i; i = nxt[i]) {
    int y = to[i];
    if (vis[y]) continue;
    mxsiz = INF, root = 0;
    totsiz = siz[y] > siz[x] ? nowsiz - siz[x] : siz[y];
    getroot(y, 0);
    // printf("%d %d\n", y, root);
    dfssub(y, 0, 1, root);
    fa[root] = x, dep[root] = dep[x] + 1;
    divide(root);
  }
}

bool yyyyy;

int main() {
#ifndef ONLINE_JUDGE
  freopen("earthquake.in", "r", stdin);
  freopen("std.out", "w", stdout);
#endif
  Input(N), Input(M);
  for (int i = 1; i <= N; ++i) Input(val[i]);
  for (int i = 1; i < N; ++i) {
    int x, y;
    Input(x), Input(y);
    adde(x, y);
    adde(y, x);
  }
  lca::init();

  mxsiz = INF, totsiz = N, root = 0;
  getroot(1, 0);
  divide(root);
  // for (int i = 1; i <= N; i++)
  //   for (int j = i; j; j = fa[j]) d[i][dep[i] - dep[j]] = lca::dis(i, j);

  int last = 0;
  while (M--) {
    int op, x, y;
    Input(op), Input(x), Input(y);
    x ^= last, y ^= last;
    cout << x << " " << y << endl;
    if (op == 0) {
      last = dist.query(dist.rt[x], 0, N, 0, y);
      for (int i = x; fa[i]; i = fa[i]) {
        int len = lca::dis(x, fa[i]);
        // int len = d[x][dep[x] - dep[fa[i]]];
        assert(y - len >= 0);
        last += dist.query(dist.rt[fa[i]], 0, N, 0, y - len);
        last -= sub.query(sub.rt[i], 0, N, 0, y - len);
      }
      cout << last << endl;
      // Print(last), Putc('\n');
    } else if (op == 1) {
      dist.modify(dist.rt[x], 0, N, 0, y - val[x]);
      for (int i = x; fa[i]; i = fa[i]) {
        int len = lca::dis(x, fa[i]);
        // int len = d[x][dep[x] - dep[fa[i]]];
        dist.modify(dist.rt[fa[i]], 0, N, len, y - val[x]);
        sub.modify(sub.rt[i], 0, N, len, y - val[x]);
      }
      val[x] = y;
    }
  }
  // cerr << (&yyyyy - &xxxxx) / 1048576.0 << "MB" << endl;
  return Flush(), 0;
}

树状数组(7784 ms)

#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;

const int _ = 1e5 + 10;
const int __ = 5e6 + 10;
const int INF = 0x3f3f3f3f;

int tot, head[_], to[_ << 1], nxt[_ << 1];

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

int N, M, val[_];

namespace lca {
int fa[_], son[_], dep[_], siz[_], top[_];
void dfs1(int x, int f) {
  siz[x] = 1;
  int maxx = 0;
  for (int i = head[x]; i; i = nxt[i]) {
    int y = to[i];
    if (y == f) continue;
    fa[y] = x, dep[y] = dep[x] + 1;
    dfs1(y, x);
    siz[x] += siz[y];
    if (siz[y] > maxx) maxx = siz[y], son[x] = y;
  }
}
void dfs2(int x, int topf) {
  top[x] = topf;
  if (!son[x]) return;
  dfs2(son[x], topf);
  for (int i = head[x]; i; i = nxt[i]) {
    int y = to[i];
    if (y == fa[x] || y == son[x]) continue;
    dfs2(y, y);
  }
}
void init() {
  dfs1(1, 0);
  dfs2(1, 1);
}
int query(int x, int y) {
  while (top[x] != top[y]) {
    if (dep[top[x]] < dep[top[y]]) swap(x, y);
    x = fa[top[x]];
  }
  return dep[x] < dep[y] ? x : y;
}
int dis(int x, int y) { return dep[x] + dep[y] - 2 * dep[query(x, y)]; }
}  // namespace lca

struct BIT {
#define lowbit(x) (x & -x)
  vector<int> c;
  int lim;
  BIT() {
    lim = 0;
    c.clear();
    c.push_back(0);
  }
  void insert(int x, int y) {
    ++x;
    for (; x <= lim; x += lowbit(x)) c[x] += y;
  }
  int query(int x) {
    ++x;
    x = min(x, lim);
    int ret = 0;
    for (; x; x -= lowbit(x)) ret += c[x];
    return ret;
  }
#undef lowbit
} dist[_], sub[_];

int mxsiz, totsiz, root;
int vis[_], siz[_], fa[_], dep[_];

void getroot(int x, int f) {
  siz[x] = 1;
  int maxx = 0;
  for (int i = head[x]; i; i = nxt[i]) {
    int y = to[i];
    if (vis[y] || y == f) continue;
    getroot(y, x);
    siz[x] += siz[y];
    maxx = max(maxx, siz[y]);
  }
  maxx = max(maxx, totsiz - siz[x]);
  if (maxx < mxsiz) mxsiz = maxx, root = x;
}

void dfsdist(int x, int f, int d, const int s) {
  dist[s].insert(d, val[x]);
  for (int i = head[x]; i; i = nxt[i]) {
    int y = to[i];
    if (vis[y] || y == f) continue;
    dfsdist(y, x, d + 1, s);
  }
}

void dfssub(int x, int f, int d, const int s) {
  sub[s].insert(d, val[x]);
  for (int i = head[x]; i; i = nxt[i]) {
    int y = to[i];
    if (vis[y] || y == f) continue;
    dfssub(y, x, d + 1, s);
  }
}

int mxdep;
void dfsmxdep(int x, int f, int d) {
  mxdep = max(mxdep, d);
  for (int i = head[x]; i; i = nxt[i]) {
    int y = to[i];
    if (vis[y] || y == f) continue;
    dfsmxdep(y, x, d + 1);
  }
}

void divide(int x) {
  vis[x] = 1;
  mxdep = 0, dfsmxdep(x, 0, 0);
  dist[x].lim = mxdep + 1;
  dist[x].c.resize(mxdep + 2, 0);
  dfsdist(x, 0, 0, x);
  int nowsiz = totsiz;
  for (int i = head[x]; i; i = nxt[i]) {
    int y = to[i];
    if (vis[y]) continue;
    mxsiz = INF, root = 0;
    totsiz = siz[y] > siz[x] ? nowsiz - siz[x] : siz[y];
    getroot(y, 0);
    mxdep = 0, dfsmxdep(y, 0, 1);
    sub[root].lim = mxdep + 1;
    sub[root].c.resize(mxdep + 2, 0);
    dfssub(y, 0, 1, root);
    fa[root] = x, dep[root] = dep[x] + 1;
    divide(root);
  }
}

int main() {
#ifndef ONLINE_JUDGE
  freopen("earthquake.in", "r", stdin);
  freopen("earthquake.out", "w", stdout);
#endif
  Input(N), Input(M);
  for (int i = 1; i <= N; ++i) Input(val[i]);
  for (int i = 1; i < N; ++i) {
    int x, y;
    Input(x), Input(y);
    adde(x, y);
    adde(y, x);
  }
  lca::init();

  mxsiz = INF, totsiz = N, root = 0;
  getroot(1, 0);
  divide(root);

  int last = 0;
  while (M--) {
    int op, x, y;
    Input(op), Input(x), Input(y);
    x ^= last, y ^= last;
    if (op == 0) {
      last = dist[x].query(y);
      for (int i = x; fa[i]; i = fa[i]) {
        int len = lca::dis(x, fa[i]);
        if (y - len < 0) continue;
        last += dist[fa[i]].query(y - len);
        last -= sub[i].query(y - len);
      }
      Print(last), Putc('\n');
    } else if (op == 1) {
      dist[x].insert(0, y - val[x]);
      for (int i = x; fa[i]; i = fa[i]) {
        int len = lca::dis(x, fa[i]);
        dist[fa[i]].insert(len, y - val[x]);
        sub[i].insert(len, y - val[x]);
      }
      val[x] = y;
    }
  }
  return Flush(), 0;
}

[ZJOI2015]幻想乡战略游戏

这个题有单独的题解 链接

BZOJ4372 烁烁的游戏

这题仿照震波的做法:

还是动态点分治的套路,对于每个点开两棵动态开点的线段树或是树状数组,以距离为下标,分别存储到点\(x\)距离为\(y\)的增量,以及到点\(fa[x]\)距离为\(y\)的增量(用于容斥)。

查询和修改不断在点分树上暴跳父亲即可。

最后,由于上次震波的惨痛教训,我决定写树状数组:

2020-01-01 18-39-45屏幕截图.png

树状数组跑的飞快,开心!

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

#ifndef ONLINE_JUDGE
bool xxx;
#endif

inline int ty() {
  char ch = getchar();
  int x = 0, f = 1;
  while (ch < '0' || ch > '9') {
    if (ch == '-') f = -1;
    ch = getchar();
  }
  while (ch >= '0' && ch <= '9') {
    x = x * 10 + ch - '0';
    ch = getchar();
  }
  return x * f;
}

const int _ = 1e5 + 10;
const int __ = 1e7 + 10;
const int INF = 0x3f3f3f3f;
int N, M, tot, head[_], to[_ << 1], nxt[_ << 1];

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

namespace lca {
int fa[_], son[_], dep[_], siz[_], top[_];
void dfs1(int x, int f) {
  siz[x] = 1;
  int maxx = 0;
  for (int i = head[x]; i; i = nxt[i]) {
    int y = to[i];
    if (y == f) continue;
    fa[y] = x, dep[y] = dep[x] + 1;
    dfs1(y, x);
    siz[x] += siz[y];
    if (siz[y] > maxx) maxx = siz[y], son[x] = y;
  }
}
void dfs2(int x, int topf) {
  top[x] = topf;
  if (!son[x]) return;
  dfs2(son[x], topf);
  for (int i = head[x]; i; i = nxt[i]) {
    int y = to[i];
    if (y == fa[x] || y == son[x]) continue;
    dfs2(y, y);
  }
}
void init() {
  dep[1] = 1, dfs1(1, 0);
  dfs2(1, 1);
}
int query(int x, int y) {
  while (top[x] != top[y]) {
    if (dep[top[x]] < dep[top[y]]) swap(x, y);
    x = fa[top[x]];
  }
  return dep[x] < dep[y] ? x : y;
}
int dis(int x, int y) { return dep[x] + dep[y] - 2 * dep[query(x, y)]; }
}  // namespace lca

struct BIT {
#define lowbit(x) (x & -x)
  vector<int> tr;
  int lim;
  void insert(int x, int y) {
    ++x;
    // assert(x<=lim);
    x = min(x, lim);
    for (; x <= lim; x += lowbit(x)) tr[x] += y;
  }
  int query(int x) {
    ++x;
    x = min(x, lim);
    int ret = 0;
    for (; x; x -= lowbit(x)) ret += tr[x];
    return ret;
  }
#undef lowbit
} dist[_], sub[_];

int mxsiz, totsiz, root, vis[_], fa[_], siz[_];

void getroot(int x, int f) {
  siz[x] = 1;
  int maxx = 0;
  for (int i = head[x]; i; i = nxt[i]) {
    int y = to[i];
    if (vis[y] || y == f) continue;
    getroot(y, x);
    siz[x] += siz[y];
    maxx = max(maxx, siz[y]);
  }
  maxx = max(maxx, totsiz - siz[x]);
  if (maxx < mxsiz) mxsiz = maxx, root = x;
}

int mxdep;

void dfs(int x, int f, int d) {
  mxdep = max(mxdep, d);
  for (int i = head[x]; i; i = nxt[i]) {
    int y = to[i];
    if (vis[y] || y == f) continue;
    dfs(y, x, d + 1);
  }
}

void divide(int x) {
  vis[x] = 1;
  int nowsiz = totsiz;
  mxdep = 0, dfs(x, 0, 0);
  dist[x].lim = mxdep + 1;
  // printf("%d %d %d\n", x, mxdep, dist[x].lim);
  dist[x].tr.resize(mxdep + 3);
  for (int i = head[x]; i; i = nxt[i]) {
    int y = to[i];
    if (vis[y]) continue;
    mxsiz = INF, root = 0;
    totsiz = siz[y] > siz[x] ? nowsiz - siz[x] : siz[y];
    getroot(y, 0);
    mxdep = 0, dfs(y, 0, 1);
    sub[root].lim = mxdep + 1;
    sub[root].tr.resize(mxdep + 2, 0);
    fa[root] = x;
    divide(root);
  }
}

int query(int x) {
  int ret = dist[x].query(dist[x].lim);
  for (int i = x; fa[i]; i = fa[i]) {
    int len = lca::dis(x, fa[i]);
    ret += dist[fa[i]].query(dist[fa[i]].lim) - dist[fa[i]].query(len - 1);
    ret -= sub[i].query(sub[i].lim) - sub[i].query(len - 1);
  }
  return ret;
}

void modify(int x, int d, int v) {
  dist[x].insert(d, v);
  // assert(d <= dist[x].lim);
  for (int i = x; fa[i]; i = fa[i]) {
    int len = lca::dis(x, fa[i]);
    if (d < len) continue;
    dist[fa[i]].insert(d - len, v);
    // assert(d - len <= dist[fa[i]].lim);
    sub[i].insert(d - len, v);
    // assert(d - len <= sub[i].lim);
  }
}

#ifndef ONLINE_JUDGE
bool yyy;
#endif

int main() {
#ifndef ONLINE_JUDGE
  freopen("game.in", "r", stdin);
  freopen("game.out", "w", stdout);
#endif
  N = ty(), M = ty();
  for (int i = 1; i < N; ++i) {
    int x = ty(), y = ty();
    adde(x, y);
    adde(y, x);
  }
  lca::init();

  mxsiz = INF, totsiz = N, root = 0;
  getroot(1, 0);
  divide(1);

  while (M--) {
    char op[5];
    int x, y, z;
    scanf("%s", op), x = ty();
    if (op[0] == 'Q')
      printf("%d\n", query(x));
    else if (op[0] == 'M') {
      y = ty(), z = ty();
      modify(x, y, z);
    }
  }

#ifndef ONLINE_JUDGE
  cerr << (&yyy - &xxx) / 1048576.0 << "MB" << endl;
#endif
  return 0;
}

[WC2014]紫荆花之恋

事实上,我学动态点分治就是为了写这道题,链接

posted @ 2019-12-31 14:30  newbielyx  阅读(201)  评论(0编辑  收藏  举报