点分树(最暴力的一集)

(维什戴尔本色出演)

前情提要

首先,你得学会点分治

引入

当你学会点分治,你会意识到点分治更多的是一种静态的问题

那么对于P6329 【模板】点分树 | 震波这道题,你会显得很无奈,它竟然要动态修改权值

P6329 【模板】点分树 | 震波

那么好,当我们意识到问题后我们就可以开始思考了,考虑到它的树结构是不变的,那么我们可不可以通过存储树结构来解决点分治的静态问题呢?

那么我们的具体时间就变成了:用点分治求出树结构,然后用点分树来解决答案问题

答案如何求

对于一个树结构,我么该如何求答案?

我们很快能想到,因为对于一个查询节点它曾经肯定是一个重心,所以我们可以计算以它为重心时,以它为线段开头的答案,不过,经过它的答案需要它的祖宗来计算。

这下犯了难,假如我们是点分治我们该如何做?

对于祖先节点,当当前节点的设定余波(这里设定余波范围就是以当前节点为子树的根时当前子树的直径的一半)可以波及到祖先节点,那么祖先节点的下一个重心肯定不是他,也就是说在设定余波的范围内不会有自己的祖先(祖宗发射器!!!)考虑设定余波的增长,我们发现按照点分治的树结构,我们的设定余波是两倍两倍增长的,假设真实余波(输入的余波)在设定余波范围内,那么祖先就不可能影响到自己,那么由于设定余波为两倍增长,假设我们暴力跳祖先,我们很快就能想到这是一个倍增,所以时间复杂度是log级别的

最后一个问题,我们暴力跳祖宗,计算答案时需要剪掉跳过来前的节点答案,以防计算bug。

不会吧,还不会?你点分治怎么学的?我不就是用树状数组等数据结构维护这个查找的答案吗?

代码

#include <iostream>
#include <vector>

using namespace std;

const int MaxN = 2e5 + 10, MaxK = 19;

int f[MaxN][MaxK], gfa[MaxN], dfn[MaxN], lg[MaxN], dep[MaxN], sz[MaxN], a[MaxN], cnt, n, m, cn, minx, rt;
vector<int> d[2][MaxN], g[MaxN];
bool vis[MaxN];

int lb(int x) {
  return x & (-x);
}

void update(int p, int op, int k, int x) {
  for (k++; k <= sz[p]; k += lb(k)) {
    d[op][p][k] += x;
  }
}

int query(int p, int op, int k, int res = 0) {
  for (k = min(sz[p], k + 1); k; k -= lb(k)) {
    res += d[op][p][k];
  }
  return res;
}

int mindep(int a, int b) {
  return dep[a] < dep[b] ? a : b;
}

void DFS(int x, int fa = 0) {
  f[++cnt][0] = x, dfn[x] = cnt;
  for (int i : g[x]) {
    if (i == fa) continue;
    dep[i] = dep[x] + 1, DFS(i, x), f[++cnt][0] = x;
  }
}

void find(int x, int fa = 0, int maxsz = 0) {
  sz[x] = 1;
  for (int i : g[x]) {
    if (vis[i] || i == fa) continue;
    find(i, x), sz[x] += sz[i], maxsz = max(maxsz, sz[i]);
  }
  maxsz = max(maxsz, n - sz[x]);
  (maxsz < minx) && (minx = maxsz, rt = x);
}

void S(int x) {
  vis[x] = 1, sz[x] = n + 1, d[0][x].resize(sz[x] + 1), d[1][x].resize(sz[x] + 1);
  for (int i : g[x]) {
    if (vis[i]) continue;
    n = sz[i], minx = 1e9, find(i), gfa[rt] = x;
    S(rt);
  }
}

int dis(int x, int y) {
  if (dfn[x] > dfn[y]) swap(x, y);
  int dx = dfn[x], dy = dfn[y], len = dy - dx + 1, lca = mindep(f[dx][lg[len]], f[dy - (1 << lg[len]) + 1][lg[len]]);
  return dep[x] + dep[y] - dep[lca] * 2;
}

void modify(int x, int w) {
  for (int i = x; i; i = gfa[i]) {
    update(i, 0, dis(x, i), w);
  }
  for (int i = x; gfa[i]; i = gfa[i]) {
    update(i, 1, dis(x, gfa[i]), w);    
  }
}

int main() {
  ios::sync_with_stdio(0), cin.tie(0);
  cin >> n >> m, cn = n;
  for (int i = 1; i <= n; i++) {
    cin >> a[i];
  }
  for (int i = 1, u, v; i < n; i++) {
    cin >> u >> v;
    g[u].push_back(v), g[v].push_back(u);
  }
  DFS(1);
  for (int j = 2; j <= cnt + 1; j++) {
    lg[j] = lg[j >> 1] + 1;
  }
  for (int j = 1; j < MaxK; j++) {
    for (int i = 1; i + (1 << j) - 1 <= cnt; i++) {
      f[i][j] = mindep(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
    }
  }
  minx = 1e9, find(1), S(rt);
  for (int i = 1; i <= cn; i++) {
    modify(i, a[i]);
  }
  for (int i = 1, op, x, k, lst = 0; i <= m; i++) {
    cin >> op >> x >> k, x ^= lst, k ^= lst;
    if (op) {
      modify(x, k - a[x]), a[x] = k;
    } else {
      lst = query(x, 0, k);
      for (int i = x; gfa[i]; i = gfa[i]) {
        (k >= dis(x, gfa[i])) && (lst += query(gfa[i], 0, k - dis(x, gfa[i])) - query(i, 1, k - dis(x, gfa[i])));
      }
      cout << lst << '\n';
    }
  }
  return 0;
}

P6626 [省选联考 2020 B 卷] 消息传递

那么这道题就很简单了,只是k内,变成了第k

代码

#include <iostream>
#include <vector>

using namespace std;

const int MaxN = 2e5 + 10, MaxK = 19;

int f[MaxN][MaxK], gfa[MaxN], dfn[MaxN], lg[MaxN], dep[MaxN], sz[MaxN], cnt, n, m, cn, minx, t, rt;
vector<int> d[2][MaxN], g[MaxN];
bool vis[MaxN];

int lb(int x) {
  return x & (-x);
}

void update(int p, int op, int k, int x) {
  (k <= sz[p]) && (d[op][p][k] += x);
}

int query(int p, int op, int k, int res = 0) {
  (k <= sz[p]) && (res += d[op][p][k]);
  return res;
}

int mindep(int a, int b) {
  return dep[a] < dep[b] ? a : b;
}

void DFS(int x, int fa = 0) {
  f[++cnt][0] = x, dfn[x] = cnt;
  for (int i : g[x]) {
    if (i == fa) continue;
    dep[i] = dep[x] + 1, DFS(i, x), f[++cnt][0] = x;
  }
}

void find(int x, int fa = 0, int maxsz = 0) {
  sz[x] = 1;
  for (int i : g[x]) {
    if (vis[i] || i == fa) continue;
    find(i, x), sz[x] += sz[i], maxsz = max(maxsz, sz[i]);
  }
  maxsz = max(maxsz, n - sz[x]);
  (maxsz < minx) && (minx = maxsz, rt = x);
}

void S(int x) {
  vis[x] = 1, sz[x] = n + 1, vector<int>().swap(d[0][x]), vector<int>().swap(d[1][x]), d[0][x].resize(sz[x] + 1), d[1][x].resize(sz[x] + 1);
  for (int i : g[x]) {
    if (vis[i]) continue;
    n = sz[i], minx = 1e9, find(i), gfa[rt] = x;
    S(rt);
  }
}

int dis(int x, int y) {
  if (dfn[x] > dfn[y]) swap(x, y);
  int dx = dfn[x], dy = dfn[y], len = dy - dx + 1, lca = mindep(f[dx][lg[len]], f[dy - (1 << lg[len]) + 1][lg[len]]);
  return dep[x] + dep[y] - dep[lca] * 2;
}

void modify(int x, int w) {
  for (int i = x; i; i = gfa[i]) {
    update(i, 0, dis(x, i), w);
  }
  for (int i = x; gfa[i]; i = gfa[i]) {
    update(i, 1, dis(x, gfa[i]), w);    
  }
}

void clear() {
  for (int i = 1; i <= n; i++) {
    vector<int>().swap(g[i]);
    vis[i] = gfa[i] = dfn[i] = dep[i] = sz[i] = 0;
    d[0][i].clear(), d[1][i].clear();
  } 
}

int main() {
  ios::sync_with_stdio(0), cin.tie(0);
    for (int j = 2; j < MaxN; j++) {
      lg[j] = lg[j >> 1] + 1;
    }
  for (cin >> t; t; t--) {
    cin >> n >> m, cn = n, cnt = 0;
    clear();
    for (int i = 1, u, v; i < n; i++) {
      cin >> u >> v;
      g[u].push_back(v), g[v].push_back(u);
    }
    DFS(1);
    for (int j = 1; j < MaxK; j++) {
      for (int i = 1; i + (1 << j) - 1 <= cnt; i++) {
        f[i][j] = mindep(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
      }
    }
    minx = 1e9, find(1), S(rt);
    for (int i = 1; i <= cn; i++) {
      modify(i, 1);
    }
    for (int i = 1, x, k, ans = 0; i <= m; i++) {
      cin >> x >> k;
      ans = query(x, 0, k);
      for (int i = x; gfa[i]; i = gfa[i]) {
        int len = k - dis(x, gfa[i]);
        (len >= 0) && (ans += query(gfa[i], 0, len) - query(i, 1, len));
      }
      cout << ans << '\n';
    }
  }
  return 0;
}

Sakurako and Chefir

给定一棵包含$$$n$$$个顶点、根节点为$$$1$$$的树。八重樱带着她的妹妹八重凛在树下散步时走神了,结果八重凛跑丢了。

为了帮助八重樱,凯文记录了他的$$$q$$$次猜测。在第$$$i$$$次猜测中,他认为八重凛在顶点$$$v_i$$$迷路了,并且拥有$$$k_i$$$点理智值

对于每次猜测,凯文假设八重凛可以沿着边进行任意次移动:

  • 从顶点$$$a$$$移动到顶点$$$b$$$时,若$$$a$$$是$$$b$$$的祖先节点$$$^{\text{∗}}$$$,则理智值保持不变;
  • 从顶点$$$a$$$移动到顶点$$$b$$$时,若$$$a$$$不是$$$b$$$的祖先节点,则八重凛的理智值会减少$$$1$$$点。

当八重凛的理智值降至$$$0$$$时,将变为拟态律者无法进行第二种移动。

你的任务是针对每个假设,计算出八重凛从顶点$$$v_i$$$出发,在拥有$$$k_i$$$点理智值时能够到达的最远顶点的距离。

$$$^{\text{∗}}$$$当顶点$$$a$$$到根节点的最短路径经过顶点$$$a$$$时,称顶点$$$a$$$是顶点$$$b$$$的祖先节点。

你需要在 4s 内完成任务,不然凯文为了减小拟态律者对于研究所的侵害,会给拟态律者秒了。

肯定能写但是再调

一些对于点分树的幻想

P10930 异象石

一开始看到这个题,我会觉得很熟悉,所以我们思考到了点分树,可是,过于暴力,导致这个是失败的

posted @ 2025-02-25 14:38  yabnto  阅读(51)  评论(0)    收藏  举报