题目链接
题意
一棵树,树上有
个点,标号为1~n,且1是树根。然后每个点都有一个值
,每条边也有一个权值
,然后让你输出每个可以控制点的数目。(比如
控制
,即树上
的权值
)
思路
首先比较好想的是暴力的方法。
预处理深度遍历树根到所有节点的权值,然后对每个点进行
,结果T。
我们在脑海中想象下遍历这棵树的过程,在DFS过程中在栈中总会呈现一条树根 到 子节点的路径,我们在这条路径上进行二分查找这条路径上 满足条件的 距当前节点最远点(也就是最靠根的节点),然后这一段路径区间都可以加1,区间修改,我们可以差分来缩减时间复杂度,只不过这是在树上差分。
有一点需要注意,差分时注意区间首尾别颠倒!
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<vector>
#include<iostream>
using namespace std;
#define ll long long
#define INF 0x3f3f3f3f
const ll N = 2e5+10;
ll d[N];//到根节点的距离
ll a[N];
int head[N],tot;
struct Edge{
int next;
int to;
ll dis;
}edge[N<<2];
inline void add(int from,int to,ll dis){
edge[++tot].next = head[from];
edge[tot].to = to;
edge[tot].dis = dis;
head[from] = tot;
}
vector<int> T;//记录路径
int c[N];//答案数组
int pre[N];//记录前驱
void dfs(int root){
int l = 0,r = T.size() - 1;
while(l < r){
int mid = l + r >> 1;
if(d[root] - d[T[mid]] <= a[root]) r = mid;
else l = mid + 1;
}
if(T.size() != 0&&d[root] - d[T[r]] <= a[root]){
c[pre[root]] ++;
c[pre[T[r]]] --;
}
T.push_back(root);
for(int i = head[root];~i;i = edge[i].next){
int y = edge[i].to;
ll dis = edge[i].dis;
d[y] = d[root] + dis;
dfs(y);
}
T.pop_back();
}
void DFS(int x){
for(int i = head[x];~i;i = edge[i].next){
int y = edge[i].to;
DFS(y);
c[x] += c[y];
}
}
int main(){
memset(head,-1,sizeof head);
int n;
scanf("%d",&n);
for(int i = 1;i <= n;++i) scanf("%lld",&a[i]);
int u;ll dis;
for(int i = 2;i <= n;++i){
scanf("%d%lld",&u,&dis);
add(u,i,dis);
pre[i] = u;
//add(i,u,dis);
}
dfs(1);
DFS(1);
for(int i = 1;i <= n;++i) cout<<c[i]<<' ';
}