树上dfs序差分+树上桶差分

如果对于一个节点p,如果子树内的点对自身有贡献,就可以在递归子树时用桶和差分来维护答案。

以这道题目为例:https://www.luogu.com.cn/problem/P1600
这个博主提供了详细题解很强:https://www.cnblogs.com/lfyzoi/p/10221884.html
总结下来有几个注意要点:

  1. 差分就是对于节点i加上贡献,在lca的父节点再把贡献删除,这样就维护了一条链上的贡献
  2. 要点:递归子树时,子节点v1产生的贡献不应该算给v2,但是不加处理的话会算进去。所以在递归v2开始时记录要用的桶的值然后算完v2减去记录的值就是答案。(因为每个点用的桶很少所以可实现)
  3. 桶要开两个,不然会算重复(因为s和t答案贡献是分开的
  4. s,t对他们的lca产生贡献时会加两次,所以要特判在ans[lca]处直接减掉
#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define ls(x) (x << 1)
#define rs(x) (x << 1 | 1)
const int N = 3e5 + 1;
vector<int> E[N];
int n, m, tot, a[N], rev[N], top[N], id[N];
int siz[N], dep[N], fa[N], hson[N];
int w[N], bu1[N*2], bu2[N*2], ans[N];
vector<pair<int, int>> xg1[N], xg2[N];
void dfs1 (int u) {
    siz[u] = 1;
    dep[u] = dep[fa[u]] + 1;
    for (auto v : E[u]) {
        if (v == fa[u]) continue;
        fa[v] = u;
        dfs1(v);
        siz[u] += siz[v];
        if (siz[v] > siz[hson[u]]) hson[u] = v;
    }
}
void dfs2(int u, int topu) {
    rev[id[u] = ++tot] = u;
    top[u] = topu;
    if (hson[u]) dfs2(hson[u], topu);
    for (int v : E[u]) {
        if (v == fa[u] || v == hson[u]) continue;
        dfs2(v, v);
    }
}
int lca(int u, int v) {
    while(top[u] != top[v]) {
        if (dep[top[u]] > dep[top[v]]) swap(u, v);
        v = fa[top[v]];
    }
    if (dep[u] > dep[v]) swap(u, v);
    return u;
}



void dfs(int x, int gs) {
    for (int v : E[x]) {
        if (v == fa[x]) continue;
        dfs(v, bu2[w[v] - dep[v] + n] + bu1[dep[v] + w[v]]);
    }
    for (auto j : xg1[x]) {
        bu1[j.first] += j.second;
    }for (auto j : xg2[x]) {
        bu2[j.first] += j.second;
    }

    ans[x] += bu1[dep[x] + w[x]] - gs + bu2[n + w[x] - dep[x]];   
}
void solve() {
    cin >> n >> m;
    for (int i = 1; i < n; i++) {
        int u, v; cin >> u >> v;
        E[u].push_back(v), E[v].push_back(u);
    }
    for (int i = 1; i <= n; i++) {
        cin >> w[i];
    }
    dfs1(1);
    dfs2(1, 1);
    for (int i = 1; i <= m; i++) {
        int s, t;
        cin >> s >> t;
        int LCA = lca(s, t);
        int dis = dep[s] + dep[t] - 2 * dep[LCA];
        xg2[t].push_back({dis - dep[t] + n, 1});
        xg2[fa[LCA]].push_back({dis - dep[t] + n, -1});
        xg1[s].push_back({dep[s], 1});
        xg1[fa[LCA]].push_back({dep[s], -1});

        if (dep[LCA] + w[LCA] == dep[s]) ans[LCA]--;
    }
    dfs(1, 0);
    for (int i = 1; i <= n; i++) cout << ans[i] << ' ';
}
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int T = 1;
    // cin >> T;
    while(T--) {
        solve();
    }
}

dfs序+树状数组差分

题目:https://www.luogu.com.cn/problem/P4219
这个比上面内容好理解一点,就是子树都是连续的编号然后用树状数组求区间和,对于时序更新问题常常离线预排序处理掉

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 7;
#define endl '\n'
#define ll long long
#define pii pair<int, int>
pii q[N];
bool que[N];
int n, m, ld[N], rd[N], tot, fa[N], dep[N], siz[N];
int t[N];
vector<int> E[N];

void dfs(int x) {
  ld[x] = ++tot;
  dep[x] = dep[fa[x]] + 1;
  siz[x] = 1;
  for (int i : E[x]) {
    if (i == fa[x]) continue;
    fa[i] = x;
    dfs(i);
    siz[x] += siz[i];
  }
  rd[x] = tot;
}
struct unifind{
  int fa[N];
  void init() {for (int i = 1; i <= n; i++) fa[i] = i;}
  int find(int x) {
    return (x == fa[x]) ? (x) : fa[x] = find(fa[x]);}
}uni;
void upd(int x, int k) {
  if (x == 0) return;
  while(x < N) {
    t[x] += k;
    x += (x & -x);
  }
}
int tque(int x) {
  int res = 0;
  while(x) {
    res += t[x];
    x -= (x & -x);
  }
  return res;
}
int pque(int l, int r) {
  return tque(r) - tque(l - 1);
}

int main() {
  // ios::sync_with_stdio(false);
  // cin.tie(nullptr);
  cin >> n >> m;
  for (int i = 1; i <= m; i++) {
    char c;
    int x, y;
    cin >> c >> x >> y;
    q[i] = {x, y};
    if (c == 'Q') 
      que[i] = 1;
    else 
      E[x].push_back(y), E[y].push_back(x);
  }
  for (int i = 1; i <= n; i++) {
    if (!ld[i]) dfs(i);
  }
  uni.init();
  for (int i =1; i <= n; i++) {
    upd(ld[i], 1); upd(ld[fa[i]], -1);
  }
  for (int i = 1; i <= m; i++) {
    int x = q[i].first, y = q[i].second;
    if (ld[x] < ld[y]) swap(x, y);
    int fy = uni.find(y);
    // cout << ld[x] << ' ' << ld[y] <<"xxx" <<endl;
    int sizx = pque(ld[x], rd[x]), sizfy = pque(ld[fy], rd[fy]);
    // cout << "YYY" << endl;
    if (!que[i]) {
      uni.fa[x] = fy;
      upd(ld[y], sizx); upd(ld[fa[fy]], -sizx);
    }
    else {
      cout << 1ll * sizx * (-1ll*sizx + 1ll*sizfy) << endl;
    }
  }
}
posted @ 2025-07-23 15:01  lyrrr  阅读(27)  评论(0)    收藏  举报