• CF E. Vasya and a Tree】 dfs+树状数组(给你一棵n个节点的树,每个点有一个权值,初始全为0,m次操作,每次三个数(v, d, x)表示只考虑以v为根的子树,将所有与v点距离小于等于d的点权值全部加上x,求所有操作完毕后,所有节点的值)


    题意:

    给你一棵n个节点的树,每个点有一个权值,初始全为0,m次操作,每次三个数(v, d, x)表示只考虑以v为根的子树,将所有与v点距离小于等于d的点权值全部加上x,求所有操作完毕后,所有节点的值

    首先要明确两件事情
    性质1.每个人的操作只会影响到他的子孙(包括自己) 性质1.每个人的操作只会影响到他的子孙(包括自己)性质1.每个人的操作只会影响到他的子孙(包括自己)
    性质2.每个人只会被他祖先的操作所影响(包括自己) 性质2.每个人只会被他祖先的操作所影响(包括自己)性质2.每个人只会被他祖先的操作所影响(包括自己)
    也就是说,如果我们能在访问到某个节点时,统计出所有影响到该节点的祖先操作 也就是说,如果我们能在访问到某个节点时,统计出所有影响到该节点的祖先操作也就是说,如果我们能在访问到某个节点时,统计出所有影响到该节点的祖先操作
    就可以统计出这个节点的最终权值 就可以统计出这个节点的最终权值就可以统计出这个节点的最终权值
    而对于每个操作,我们只要用一个dep数组保存每个深度被增加的值 而对于每个操作,我们只要用一个dep数组保存每个深度被增加的值而对于每个操作,我们只要用一个dep数组保存每个深度被增加的值
    所有深度大于当前节点的操作都会影响到当前节点,如果用线段树就是一个区间求和问题 所有深度大于当前节点的操作都会影响到当前节点,如果用线段树就是一个区间求和问题所有深度大于当前节点的操作都会影响到当前节点,如果用线段树就是一个区间求和问题
    为了减少代码量我们用树状数组,更新时只在本次操作的最深的深度更新 为了减少代码量我们用树状数组,更新时只在本次操作的最深的深度更新为了减少代码量我们用树状数组,更新时只在本次操作的最深的深度更新
    这样求一个1−maxdep的前缀和就是所有更新了根节点的操作 这样求一个1-maxdep的前缀和就是所有更新了根节点的操作这样求一个1−maxdep的前缀和就是所有更新了根节点的操作
    在求一个1−(nowdep−1)的前缀和就是所有不包含当前节点的操作 在求一个1-(nowdep-1)的前缀和就是所有不包含当前节点的操作在求一个1−(nowdep−1)的前缀和就是所有不包含当前节点的操作
    两个前缀和相减就是当前节点被更新的值 两个前缀和相减就是当前节点被更新的值两个前缀和相减就是当前节点被更新的值
    为了保证每个操作只影响自己子树内的节点,在dfs退出子树时 为了保证每个操作只影响自己子树内的节点,在dfs退出子树时为了保证每个操作只影响自己子树内的节点,在dfs退出子树时
    要将当前根节点的所有修改值还原 要将当前根节点的所有修改值还原要将当前根节点的所有修改值还原

    #include<stdio.h>
    #include<iostream>
    #include<algorithm>
    #include<vector>
    using namespace std;
    typedef long long ll;
    const int maxn = 3e5+10;
    int n,m;
    ll tree[maxn],ans[maxn];
    vector<int> G[maxn],D[maxn],X[maxn];
    void add(int x,int val)
    {
        while(x<=n)
        {
            tree[x]+=val;
            x=x+(x&-x);
        }
    }
    ll sum(int x)
    {
        ll ans=0;
        while(x)
        {
            ans+=tree[x];
            x=x-(x&-x);
        }
        return ans;
    }
    void dfs(int x,int fa,int dep)
    {
        for(int i=0;i<D[x].size();i++)
        {
            add(min(D[x][i]+dep,n),X[x][i]);//进子树之前更新
        }
        ans[x]=sum(n)-sum(dep-1);//树状数组变区间查询为两个前缀和相减
        //由于性质2,所以在这个地方就可以直接算出当前节点的最终答案
        for(int i=0;i<G[x].size();i++)
        {
            if(G[x][i]==fa) continue;
            dfs(G[x][i],x,dep+1);
        }
        for(int i=0;i<D[x].size();i++)
        {
            add(min(D[x][i]+dep,n),-X[x][i]);//出子树之后还原
        }
    }
    int main()
    {
        int x,y,z;
        scanf("%d",&n);
        for(int i=1;i<=n-1;i++)
        {
            scanf("%d%d",&x,&y);
            G[x].push_back(y);
            G[y].push_back(x);
        }
        scanf("%d",&m);
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d%d",&x,&y,&z);
            D[x].push_back(y);
            X[x].push_back(z);
        }
        dfs(1,0,1);
        for(int i=1;i<=n;i++)
        {
            printf("%lld ",ans[i]);
        }
        return 0;
    }
    View Code
  • 相关阅读:
    05 | 深入浅出索引(下)
    04 | 深入浅出索引(上)
    03 | 事务隔离:为什么你改了我还看不见?
    02 | 日志系统:一条SQL更新语句是如何执行的?
    01 | 基础架构:一条SQL查询语句是如何执行的?
    orm的惰性机制
    简易的迁移
    rails 中 preload、includes、Eager load、Joins 的区别
    换种方式去分页
    Scala function programming
  • 原文地址:https://www.cnblogs.com/shuaihui520/p/9960850.html
Copyright © 2020-2023  润新知