loj144&145 dfs序+树状数组/线段树
https://loj.ac/p/144
https://loj.ac/p/145
两题非常相似,一题的权值修改是在点上的,另一题的权值修改是在整棵子树上的。
首先我们要了解dfs序,简单点理解,就是树在dfs时遍历到的次序,dfs时记录每个节点的子树大小sz。dfs序有个非常重要的性质,子树在dfs序上是连续的,意思就是,对于一个节点,在dfs序上sz长的区间全都是他的子节点以及他自己。
所以我们将一棵树映射到了一个区间上,并且可以发现,两题分别是点修改+区间查询和区间修改+区间查询,考虑树状数组和线段树。
//loj144
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int mod = 998244353, INF = 1 << 30;
const int N = 1e6 + 10;
vector<int> e[N];
LL v[N], sz[N];
int dfn[N];
int n, m, r, cnt;
LL c[N]; //c数组是指每个下标管理的区间和
//点修 + 区间和
int lowbit(int x)
{
return x & (-x);
}
void add(int p, LL k)
{
for (; p <= n; p += lowbit(p)){
c[p] += k;
}
}
LL ask(int p) // 求[1, p]的前缀和
{
LL s = 0;
for (; p > 0; p -= lowbit(p)){
s += c[p];
}
return s;
}
void dfs(int u, int fa)
{
sz[u] = 1;
dfn[u] = ++ cnt;
for (int v: e[u]){
if (v == fa) continue;
dfs(v, u);
sz[u] += sz[v];
}
}
int main()
{
cin.tie(nullptr);
ios::sync_with_stdio(false);
cin >> n >> m >> r;
for (int i = 1; i <= n; i ++){
cin >> v[i];
}
for (int i = 1; i < n; i ++){
int a, b;
cin >> a >> b;
e[a].push_back(b);
e[b].push_back(a);
}
dfs(r, 0);
for (int i = 1; i <= n; i ++){
add(dfn[i], v[i]);
}
while (m --){
int op; cin >> op;
if (op == 1){
int a;
LL x;
cin >> a >> x;
add(dfn[a], x);
}
else if (op == 2){
int a;
cin >> a;
LL ans = ask(dfn[a] + sz[a] - 1) - ask(dfn[a] - 1);
cout << ans << endl;
}
}
return 0;
}
//loj145
#include<bits/stdc++.h>
using namespace std;
#define lc p << 1
#define rc p << 1 | 1
typedef long long LL;
typedef pair<int, int> PII;
const int mod = 998244353, INF = 1 << 30;
const int N = 1e6 + 10;
int w[N];
int dfn[N], sz[N], v[N];
int n, m, r, cnt;
vector<int> e[N];
struct node{
int l, r;
LL sum, add;
}tr[N * 4];
// 区间修改 + 区间查询
void build(int p, int l, int r)
{
tr[p] = {l, r, w[l], 0};
if (l == r) return;
int m = (l + r) >> 1;
build(lc, l, m);
build(rc, m + 1, r);
tr[p].sum = tr[lc].sum + tr[rc].sum;
}
void pushup(int p)
{
tr[p].sum = tr[lc].sum + tr[rc].sum;
}
void pushdown(int p) // 懒节点下传
{
if (tr[p].add){
tr[lc].sum += tr[p].add * (tr[lc].r - tr[lc].l + 1);
tr[rc].sum += tr[p].add * (tr[rc].r - tr[rc].l + 1);
tr[lc].add += tr[p].add;
tr[rc].add += tr[p].add;
tr[p].add = 0;
}
}
LL query(int p, int x, int y)
{
if (x <= tr[p].l && tr[p].r <= y) return tr[p].sum;
int m = (tr[p].l + tr[p].r) >> 1;
pushdown(p);
LL sum = 0;
if (x <= m) sum += query(lc, x, y);
if (y > m) sum += query(rc, x, y);
return sum;
}
void update(int p, int x, int y, LL k) //区间更新
{
if (x <= tr[p].l && tr[p].r <= y){
tr[p].sum += (tr[p].r - tr[p].l + 1) * k;
tr[p].add += k;
return;
}
int m = (tr[p].l + tr[p].r) >> 1;
pushdown(p);
if (x <= m) update(lc, x, y, k);
if (y > m) update(rc, x, y, k);
pushup(p);
}
void dfs(int u, int fa)
{
sz[u] = 1;
dfn[u] = ++ cnt;
for (int v: e[u]){
if (v == fa) continue;
dfs(v, u);
sz[u] += sz[v];
}
}
signed main()
{
cin.tie(nullptr);
ios::sync_with_stdio(false);
cin >> n >> m >> r;
for (int i = 1; i <= n; i ++){
cin >> v[i];
}
for (int i = 1; i < n; i ++){
int a, b;
cin >> a >> b;
e[a].push_back(b);
e[b].push_back(a);
}
dfs(r, 0);
for (int i = 1; i <= n; i ++) w[dfn[i]] = v[i];
build(1, 1, n);
while (m --){
int op; cin >> op;
if (op == 1){
int a;
LL x;
cin >> a >> x;
update(1, dfn[a], dfn[a] + sz[a] - 1, x);
}
else if (op == 2){
int a;
cin >> a;
LL ans = query(1, dfn[a], dfn[a] + sz[a] - 1);
cout << ans << endl;
}
}
return 0;
}