定義
每一個節點記錄:
st[i]:第一次遍歷到這個點的時間戳
ed[i]:離開這個點的時候的時間戳
dfs序是一個連續的序列,L=st[u],R=ed[u],把樹上操作轉移到序列中
dfs序的七種常見模型
點修改+子樹和查詢
(實際上是樹狀陣列的操作)
單點修改
add(st[x], +-v)
查詢子樹和
ask(ed[x])-ask(st[x-1])
第1題 樹查詢 檢視測評資料資訊
有一棵樹,含有n個節點,和n-1條邊。
這棵樹的根節點編號為root,每個節點都有一個權值,第i個節點的權值為v[i]。
現在有m個操作,每個操作有如下兩種型別:
1 a x :表示將節點a的權值加上x
2 a :表示求a節點的子樹上所有節點的和(包括a節點本身的權重)。
輸入格式
第一行給出三個正整數n,m,root,表示樹的節點數、操作次數、和這棵樹的根節點
第二行給出n個正整數,第2個正整數表示第i個節點的權值v[i]
下面n-1行每行兩個正整數u,v,表示邊的兩個端點
接下來m行,每行給出一個操作
部分資料:1<=n,m<=100
1<=n,m<=1e6, 1<=root<=n
1<=u,v,a<=n
-1e6<=v[i],x<=1e6
輸出格式
對於每個型別為2的操作,輸出一行一個正整數,表示以為根的子樹的所有節點的權值和
輸入/輸出例子1
輸入:
5 6 1
1 2 3 4 5
1 3
1 2
2 4
2 5
1 2 10
1 3 10
1 4 5
1 5 1
2 3
2 2
輸出:
13
27
樣例解釋
無
#include <bits/stdc++.h> #define int long long using namespace std; const int N=1e6+5; int n, m, root, v[N], u1, v1, x, k, op; int dfn[N], st[N], ed[N], tim=0, s[N]; vector<int> a[N]; void dfs(int u, int fa) { st[u]=++tim; dfn[tim]=u; for (int i=0; i<a[u].size(); i++) { int v=a[u][i]; if (v!=fa) dfs(v, u); } ed[u]=tim; } int lowbit(int x) { return x&(-x); } void add(int x, int k) { for (int i=x; i<=n; i+=lowbit(i)) s[i]+=k; } int ask(int x, int y) { int res=0; for (int i=y; i>=1; i-=lowbit(i)) res+=s[i]; for (int i=x-1; i>=1; i-=lowbit(i)) res-=s[i]; return res; } signed main() { scanf("%d%d%d", &n, &m, &root); for (int i=1; i<=n; i++) scanf("%lld", &v[i]); for (int i=1; i<n; i++) { scanf("%d%d", &u1, &v1); a[u1].push_back(v1); a[v1].push_back(u1); } dfs(root, -1); for (int i=1; i<=n; i++) add(st[i], v[i]); while (m--) { scanf("%d", &op); if (op==1) { scanf("%d%lld", &x, &k); add(st[x], k); } else if (op==2) { scanf("%d", &x); printf("%lld\n", ask(st[x], ed[x])); } } return 0; }