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] + delta,node[v] + delta,node[lca(u, v)] - delta,node[father[lca(u, v)]] - delta
单点修改
信息设计
-
节点信息:
delta:修改的总和mid:存的这玩意儿:\((\text{depth}[u] + 1)\times \text{delta}\)
-
信息的封闭性:(会不会丢失信息):显然
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;
}
然后就是查询
-
op == 2,查询a的节点值:query(in[a], out[a]).delta -
op == 3,记M = query(in[a], out[a]).mid,D = query(in[a], out[a]).delta\(\text{ans} = \text M - \text{depth}(a) \times \text D\)
注意LCA需要使用更快地方式(Tarjan)
代码:
.
\[\LARGE{\textcolor{red}{\mathscr{OVER}}}
\]

浙公网安备 33010602011771号