DFS 序 plus 树上·差分

DFS 序 plus 树上·差分


概-括

将树上问题转化成序列问题


例-题

LibreOJ #144. DFS 序1

题面

纯板子

in[u] (时间戳) 表示节点 u 第一次进入递归栈的时间,out[u] 表示出栈的时间

void DFS(int u) {
	in[u] = ++id;
	dfs[id] = u;
	vis[u] = true;
	for (int v : adj[u])
		if (!vis[v])
			DFS(v);
	out[u] = id;
}

所以节点的修改变为对其时间戳所在位置的修改

update(in[u], in[u], d)

查询就是对 [in[u], out[u]] 的查询

query(in[u], out[u])

代码:

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

using lf = double;
using ll = long long;
using ull = unsigned long long;

const int maxn = 1e6 + 5;
ll ls(ll i) {return i << 1;}
ll rs(ll i) {return i << 1 | 1;}

int n, m, root, id;
ll a[maxn];
ll tree[maxn << 2], lazy_tag[maxn << 2];
ll in[maxn], out[maxn], dfs[maxn]; // dfn + out
bool vis[maxn];
vector<int> adj[maxn];
int v[maxn];

void maintain(ll id) { // push up
	tree[id] = tree[ls(id)] + tree[rs(id)];
}

void build(ll id, ll left, ll right) {
	lazy_tag[id] = 0;
	if (left == right) {
		tree[id] = v[dfs[left]];
		return;
	}
	ll mid = (left + right) >> 1;
	build(ls(id), left, mid);
	build(rs(id), mid + 1, right);
	maintain(id);
}

void addtag(ll d, ll id, ll left, ll right) {
	lazy_tag[id] += d;
	tree[id] += d * (right - left + 1);
}

void pushdown(ll id, ll left, ll right) {
	if (lazy_tag[id]) {
		ll mid = (left + right) >> 1;
		addtag(lazy_tag[id], ls(id), left, mid);
		addtag(lazy_tag[id], rs(id), mid + 1, right);
		lazy_tag[id] = 0;
	}
}

void update(ll L, ll R, ll d, ll id = 1, ll left = 1, ll right = n) {
	if (L <= left && right <= R) {
		addtag(d, id, left, right);
		return;
	}
	pushdown(id, left, right);
	ll mid = (left + right) >> 1;
	if (L <= mid) update(L, R, d, ls(id), left, mid);
	if (R > mid) update(L, R, d, rs(id), mid + 1, right);
	maintain(id);
}

ll query(ll L, ll R, ll id = 1, ll left = 1, ll right = n) {
	if (L <= left && right <= R)
		return tree[id];
	pushdown(id, left, right);
	ll mid = (left + right) >> 1;
	ll res = 0;
	if (L <= mid) res = res + query(L, R, ls(id), left, mid);
	if (R > mid) res = res + query(L, R, rs(id), mid + 1, right);
	return res;
}

void DFS(int u) {
	in[u] = ++id;
	dfs[id] = u;
	vis[u] = true;
	for (int v : adj[u])
		if (!vis[v])
			DFS(v);
	out[u] = id;
}

void solve() {

	cin >> n >> m >> root;

	for (int i = 1; i <= n; i++) cin >> v[i];
	for (int i = 1; i < n; i++) {
		int u, v;
		cin >> u >> v;
		adj[u].push_back(v);
		adj[v].push_back(u);
	}

//	vis[root] = 1;
	DFS(root);
	build(1, 1, n);

	for (int i = 1; i <= m; i++) {
		int op;
		cin >> op;
		int r;
		switch (op) {
			case 1: int x;cin >> r >> x;update(in[r], in[r], x);break;
			case 2: cin >> r;cout << query(in[r], out[r]) << "\n";break;
			default: break;
		}
	}

}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);

	int T = 1;
	while (T--) solve();

	return 0;
}

LibreOJ #145. DFS 序2

题面

同上道题....

但是建树有点不同:

tree[id] = v[dfs[left]]

其中 dfs 是 dfs 序

代码:

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

using lf = double;
using ll = long long;
using ull = unsigned long long;

int read() {
	int k = 0, f = 1;
	char c = getchar();
	while (c < '0' || c > '9') {
		if (c == '-')f = -1;
		c = getchar();
	}
	while (c >= '0' && c <= '9')k = k * 10 + c - '0', c = getchar();
	return k * f;
}
void print(ll x) {
	if (x < 0)putchar('-'), x = -x;
	if (x < 10)putchar(x + '0');
	else print(x / 10), putchar(x % 10 + '0');
}

const int maxn = 1e6 + 5;
ll ls(ll i) {return i << 1;}
ll rs(ll i) {return i << 1 | 1;}

int n, m, root, id;
ll a[maxn];
ll tree[maxn << 2], lazy_tag[maxn << 2];
ll in[maxn], out[maxn], dfs[maxn]; // dfn + out + dfs
bool vis[maxn];
vector<int> adj[maxn];
int v[maxn];

void maintain(ll id) { // push up
	tree[id] = tree[ls(id)] + tree[rs(id)];
}

void build(ll id, ll left, ll right) {
	lazy_tag[id] = 0;
	if (left == right) {
		tree[id] = v[dfs[left]];
		return;
	}
	ll mid = (left + right) >> 1;
	build(ls(id), left, mid);
	build(rs(id), mid + 1, right);
	maintain(id);
}

void addtag(ll d, ll id, ll left, ll right) {
	lazy_tag[id] += d;
	tree[id] += d * (right - left + 1);
}

void pushdown(ll id, ll left, ll right) {
	if (lazy_tag[id]) {
		ll mid = (left + right) >> 1;
		addtag(lazy_tag[id], ls(id), left, mid);
		addtag(lazy_tag[id], rs(id), mid + 1, right);
		lazy_tag[id] = 0;
	}
}

void update(ll L, ll R, ll d, ll id = 1, ll left = 1, ll right = n) {
	if (L <= left && right <= R) {
		addtag(d, id, left, right);
		return;
	}
	pushdown(id, left, right);
	ll mid = (left + right) >> 1;
	if (L <= mid) update(L, R, d, ls(id), left, mid);
	if (R > mid) update(L, R, d, rs(id), mid + 1, right);
	maintain(id);
}

ll query(ll L, ll R, ll id = 1, ll left = 1, ll right = n) {
	if (L <= left && right <= R)
		return tree[id];
	pushdown(id, left, right);
	ll mid = (left + right) >> 1;
	ll res = 0;
	if (L <= mid) res = res + query(L, R, ls(id), left, mid);
	if (R > mid) res = res + query(L, R, rs(id), mid + 1, right);
	return res;
}

void DFS(int u) {
	in[u] = ++id;
	dfs[id] = u;
	vis[u] = true;
	for (int v : adj[u])
		if (!vis[v])
			DFS(v);
	out[u] = id;
}

void solve() {
	
	n = read();
	m = read();
	root = read();
	
	for (int i = 1; i <= n; i++) cin >> v[i];
	for (int i = 1; i < n; i++) {
		int u = read(), v = read();
		adj[u].push_back(v);
		adj[v].push_back(u);
	}
	
	//	vis[root] = 1;
	DFS(root);
	build(1, 1, n);
	
	for (int i = 1; i <= m; i++) {
		int op = read();
		if (op == 1) {int r = read(), x = read();update(in[r], out[r], x);}
		else {int r = read();print(query(in[r], out[r]));putchar('\n');}
	}
	
}

int main() {
	int T = 1;
	while (T--) solve();
	
	return 0;
}

快读里面要用 long long


LibreOJ #146. DFS 序 3,树上差分 1

题面

树上差分

修改:

u, v \(\to\)

node[u] + deltanode[v] + deltanode[lca(u, v)] - deltanode[father[lca(u, v)]] - delta

单点修改

信息设计

  1. 节点信息:

    • delta :修改的总和
    • mid :存的这玩意儿:\((\text{depth}[u] + 1)\times \text{delta}\)
  2. 信息的封闭性:(会不会丢失信息):显然

pushup 的写法显然:

void maitain(int id) {
    tree[id].delta = tree[ls(id)].delta + tree[rs(id)].delta;
    tree[id].mid = tree[ls(id)].mid + tree[rs(id)].mid;
}

然后就是查询

  1. op == 2,查询 a 的节点值:query(in[a], out[a]).delta

  2. op == 3,记 M = query(in[a], out[a]).midD = query(in[a], out[a]).delta

    \(\text{ans} = \text M - \text{depth}(a) \times \text D\)

注意LCA需要使用更快地方式(Tarjan

代码:


.

\[\LARGE{\textcolor{red}{\mathscr{OVER}}} \]

posted @ 2025-12-26 20:02  Yangyihao  阅读(3)  评论(0)    收藏  举报