Loading

【完结】树上差分学习笔记+做题记录

树上差分

点的差分

  • 求路径 \(u-v\) 上的点被经过的次数.
  • \(cnt[x]\) 表示点 \(x\) 被经过的次数.
  • 核心代码:
cnt[u]++;
cnt[v]++;
cnt[lca(u,v)]--;
cnt[father[lca(u,v)]]--;

边的差分

  • \(u-v\) 路径上每条边的经过次数
  • \(cnt[x]\) :代表 \(x\) 向上的边经过的次数.
  • 核心代码:
cnt[u]++;
cnt[v]++;
cnt[lca(u,v)]-=2;

A. 运输压力

解法

树上查分板子题啊

#include <bits/stdc++.h>
using namespace std;
const int N = 5e4 + 20, L = 20;
int n, k, x, y, m;
int lg[N], fa[N][L], dep[N];
int pre[N], cnt[N], maxx;
struct node {
    int to, nxt;
} a[N << 1];
void add(int u, int v) {
    a[++k] = {v, pre[u]};
    pre[u] = k;
}
void dfs(int x, int fath) {
    dep[x] = dep[fath] + 1;
    fa[x][0] = fath;
    for (int i = pre[x]; i; i = a[i].nxt) {
        int to = a[i].to;
        if (to != fath) {
            dfs(to, x);
        }
    }
}
int lca(int u, int v) {
    if (dep[u] < dep[v])
        swap(u, v);
    while (dep[u] > dep[v]) {
        u = fa[u][lg[dep[u] - dep[v]]];
    }
    if (u == v)
        return u;
    for (int i = L - 1; i >= 0; i--) {
        if (fa[u][i] != fa[v][i]) {
            u = fa[u][i];
            v = fa[v][i];
        }
    }
    return fa[u][0];
}
void find_max(int x, int fath) {
    for (int i = pre[x]; i; i = a[i].nxt) {
        int to = a[i].to;
        if (to != fath) {
            find_max(to, x);
            cnt[x] += cnt[to];  // 从叶到根!
        }
    }
    if (maxx < cnt[x])
        maxx = cnt[x];
}
int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i < n; i++) {
        scanf("%d%d", &x, &y);
        add(x, y);
        add(y, x);
    }
    dfs(1, 0);
    for (int i = 2; i <= n; i++) lg[i] = lg[i >> 1] + 1;
    for (int i = 1; i < L; i++) {
        for (int j = 1; j <= n; j++) {
            fa[j][i] = fa[fa[j][i - 1]][i - 1];
        }
    }
    while (m--) {
        scanf("%d%d", &x, &y);
        cnt[x]++;
        cnt[y]++;
        int lc = lca(x, y);
        cnt[lc]--;
        cnt[fa[lc][0]]--;
    }
    find_max(1, 0);
    printf("%d\n", maxx);
}

B.P3258 [JLOI2014] 松鼠的新家

直接板子。

#include <bits/stdc++.h>
using namespace std;
const int N = 3e5+10, L = 20;
int pre[N], dep[N], fa[N][L], lg[N], n, k,op[N];
int x, y, org[N], ans;
struct node {
	int to, nxt;
} a[2 * N];
void add(int u, int v) {
	a[++k] = {v, pre[u]};
	pre[u] = k;
}
void dfs(int x, int fath) {
	fa[x][0] = fath;
	dep[x] = dep[fath] + 1;
	for (int i = pre[x]; i; i = a[i].nxt) {
		int to = a[i].to;
		if (to != fath) {
			dfs(to, x);
			org[x] += org[to];
		}
	}
}
int lca(int u, int v) {
	if (dep[u] < dep[v]) {
		swap(u, v);
	}
	while (dep[u] > dep[v]) {
		u = fa[u][lg[dep[u] - dep[v]]];
	}
	if (u == v) {
		return u;
	}
	for (int i = L - 1; i >= 0; i--) {
		if (fa[u][i] != fa[v][i]) {
			u = fa[u][i];
			v = fa[v][i];
		}
	}
	return fa[u][0];
}
int main() {
	cin >> n;
	for(int i = 1;i <= n;i++){
		cin>>op[i];
	}
	for (int i = 1; i < n; i++) {
		cin >> x >> y;
		add(x, y);
		add(y, x);
	}
	
	int m = n - 1;
	for (int i = 2; i <= n; i++) {
		lg[i] = lg[i >> 1] + 1;
	}
	dfs(1, 0);
	for (int i = 1; i < L; i++) {
		for (int j = 1; j <= n; j++) {
			fa[j][i] = fa[fa[j][i - 1]][i - 1];
		}
	}
	for (int i = 1;i < n;i++) {
		x = op[i],y = op[i+1];
		int lc = lca(x, y);
		org[x]++;
		org[y]++;
		org[lc] -= 1;
		org[fa[lc][0]] -= 1;
	}
	dfs(1,0);
	//除第一个点以外,每个点被多访问了一次
	for(int i = 2;i <= n;i++){
		org[op[i]]--;
	}
	for(int i = 1;i <= n;i++){
		printf("%d\n",org[i]);
	}
	return 0;
}
posted @ 2025-03-27 21:18  FrankWkd  阅读(18)  评论(0)    收藏  举报