J - Vasya and a Tree CodeForces - 1076E树状数组+dfs

传送

题意:

有一棵树,1是根节点,给出三个数x,d,v,表示以x为节点的子树中,深度小于等于d的每个节点值+v;

这题的难点在于,如何控制深度d;

先说我没做出来的思路:

1 如果我们直接以深度建立树状数组,在改变以2为根节点的子树的时候,同样深度为2的2,3会同时更改,但是3的并不在2的子树上;我们还要判断是否在一个子树上,就十分的复杂。

2 如果是直接用dfs序,就像上面所说,深度问题,假设2-3-4是一条路,2-5是另一条,2-6-7-8又是一条,深度<=1的都被改变,实现起来就很麻烦

AC思路:

不如从答案出发,分析每个点的更改的情况

根据题意我们得知,每个节点只有与他直线相连且深度比他小(不会形容)的节点才能改变他

我们可以直接用dfs一个点一个点的求值,dfs的特点想必大家都知道,他走过的节点符合我们上面说的情况,也就是说,他在路中走过的节点都可以更改当前节点(其余的任何节点都不会改变当前节点)

所以我们只需要走一步算一步(进行走的节点的改值操作)就可以了,

回溯时(更改路),也要对上一步操作撤回(增加的值减去,减去的值增加)。

这个跟第一个一样都是用深度解决问题,但是这个方法在dfs中就完全没有了第一种方法的顾虑,因为dfs搜索时走的节点一定是同一个子树的节点。

代码:

#include<stdio.h>
#include<algorithm>
#include<math.h>
using namespace std;
#include<string.h>
#include<iostream>
#include<vector>
#define ll long long
ll val[300100],biao[300100],deep[300100];
ll n;
vector<vector<ll> >e(300100);
vector<pair<ll,ll> >h[300100];
ll lowbit(ll x)
{
    return x&(-x);
}
void add(ll x,ll v)
{
    for(ll j=x; j<=n; j+=lowbit(j))
        val[j]+=v;
}
ll getsum(ll x)
{
    ll ans=0;
    for(ll j=x; j>0; j-=lowbit(j))
        ans+=val[j];
    return ans;
}
void DFS(ll x,ll step,ll fa)
{
    for(ll i=0; i<h[x].size(); i++)
    {
        ll d=h[x][i].first;
        ll v=h[x][i].second;
        add(step,v);
        add(step+d+1,-v);
    }
    deep[x]=getsum(step);

    for(ll i=0; i<e[x].size(); i++)
    {
        ll cnt=e[x][i];
        if(cnt==fa)continue;
        if(biao[cnt]==0)
        {
            biao[cnt]=1;
            DFS(cnt,step+1,x);
            biao[cnt]=0;
        }
    }
    for(ll i=0; i<h[x].size(); i++)
    {
        ll d=h[x][i].first;
        ll v=h[x][i].second;
        add(step,-v);
        add(step+d+1,v);
    }
}
int main()
{

    ll m;
    scanf("%lld",&n);
    ll x,y,d,v;
    for(ll i=1; i<n; i++)
    {
        scanf("%lld %lld",&x,&y);
        e[x].push_back(y);
        e[y].push_back(x);
    }
    memset(val,0,sizeof(val));
    scanf("%lld",&m);
    for(ll i=0; i<m; i++)
    {
        scanf("%lld %lld %lld",&x,&d,&v);
        h[x].push_back({d,v});
    }
    DFS(1,1,0);
    for(ll i=1; i<=n; i++)
        printf("%lld ",deep[i]);
    printf("\n");
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值