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;
}
posted @ 2023-11-22 09:06  lu1no  阅读(64)  评论(0编辑  收藏  举报