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;
}