J - Whistle's New Car( 树上差分 )

J - Whistle's New Car( 树上差分 )

题目链接:https://vjudge.net/problem/Gym-101147J

题意:

有n个城市,每个城市有一个权值,表示在这个城市的加油站可以加多少油。

现在要计算每个城市i,有多少个城市j可以到达它:

① j 是 i 的子树。

② 在城市 j 加满Xj的油后不再加油能到达 i 城市。

 

思路:

考虑当前点i只对往上一条链有贡献,就dfs保存到当前点之上的所有父亲点,二分查找到哪个点停住,差分维护。


我们从根结点出发,dfs整棵树,在dfs的过程中,我们维护一个道路长度的和sum[],sum[j]就是1到第j个城市的路径之和。

假如我们现在dfs到了第j个城市v,此时1~j的路径之和就是sum[j],然后sum[j]-x[v]就是从v城市出发所能到达的最远距离。

int pos=lower_bound(sum,sum+ret+1,sum[ret]-x[v])-sum;

接下来二分查找计算出从v出发在这条路径上所能到达的最远的城市。它所能到达的城市都需要+1。

最后把子节点的个数加到父节点上即可。

 

代码:

#include <bits/stdc++.h>
#define int long long

using namespace std;

const int maxn = 2e6+10;
int head[maxn],cnt;
struct node {
    int to,w,nxt;
}e[maxn];
int n,d[maxn],sum[maxn],ans[maxn],id[maxn],tot;

void addage( int u, int v, int w )
{
    e[cnt].to = v;
    e[cnt].w = w;
    e[cnt].nxt = head[u];
    head[u] = cnt++;
}

void dfs( int u, int fa )
{
    for ( int i=head[u]; i!=-1; i=e[i].nxt ) {
        int v = e[i].to, w=e[i].w;
        if ( v==fa ) continue ;
        sum[tot] = sum[tot-1] + w;
        id[tot] = v;
        int pos = lower_bound(sum,sum+tot,sum[tot]-d[v]) - sum;
        if ( pos<tot ) {
            ans[ u ] ++;
            ans[ id[pos-1] ] --;
        }
        tot ++;
        dfs(v,u);
        tot --;
        ans[u] += ans[v];
    }

}

signed main()
{
    freopen("car.in","r",stdin);
    int t;cin>>t;
    while ( t-- ) {
        memset(head,-1,sizeof(head));cnt=0;
        memset(ans,0,sizeof(ans));
        cin >> n;
        for ( int i=1; i<=n; i++ ) scanf("%lld",&d[i]);
        for ( int i=1; i<n; i++ ) {
            int u,v,w;scanf("%lld %lld %lld",&u,&v,&w);
            addage(u,v,w);addage(v,u,w);
        }
        sum[0]=0; id[0]=1;
        tot = 1;
        dfs(1,1);
        for ( int i=1; i<n; i++ ) printf("%lld ",ans[i]);
        printf("%lld\n",ans[n]);
    }

    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值