题面
题目描述
给定一棵有根树,树上一共有 n 个节点,节点的编号是 1∼n,其中根节点的编号为 1,每个节点有一个值 ai,请你求出对于每个节点,它的子树中有多少个节点到它的最短 距离不超过 ai。
输入格式
第一行一个整数 n 表示树的节点个数。
第二行 n 个整数 a1,a2,⋅⋅⋅,an 用空格隔开。
接下来 n−1 行每行两个整数 u,v 表示节点 u 和节点 v 存在一条边。
输出格式
输出一行 n 个整数表示答案,用空格隔开。
输入输出样例
输入 #1复制
3 1 1 1 1 2 1 3
输出 #1复制
2 0 0
输入 #2复制
9 2 3 3 3 3 3 3 3 3 1 2 1 3 3 4 3 5 4 6 4 7 5 8 8 9
输出 #2复制
4 0 6 2 2 0 0 1 0
说明/提示
对于所有测试点:1≤n≤10^5,1≤ai≤20。
测试点编号 | n≤ |
---|---|
1∼5 | 10 |
6∼10 | 10^3 |
11∼20 | 10^5 |
思路
这道题我们先看一下数据大小,n≤10^5,但是 ai≤20。很显然,我们可以利用这个 “bug” 只要循环n次,每次 dfs a[ i ] 也是不会超的。但因为要求计算子树上的权值,所以要先记录 father 数组。
这时候很多人就会被题目套路(包括我),以为根节点为 1 就说明每条输入边的第一个值就是 父亲,第二个就是孩子。题目又没告诉你这条信息!
所以我们还是要先 dfs 一遍 father,再开始计算。
时间复杂度 O(n*a)
函数代码
void dfs1(int u){
for(auto v:e[u]){
if(v!=fa[u]){
fa[v]=u;
dfs1(v);
}
}
}
void dfs(int u,int f,int s,int step){
ans[u]=1;
if(step>s){
ans[u]=0;
return;
}
if(step==s||e[u].size()==1)return;
for(int i=0;i<e[u].size();i++){
int v=e[u][i];
if(v!=f){
dfs(v,u,s,step+1);
ans[u]+=ans[v];
}
}
}
主函数代码
// freopen("tree.in","r",stdin);
// freopen("tree.out","w",stdout);
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<n;i++){
int u,v;
cin>>u>>v;
e[u].push_back(v);
e[v].push_back(u);
}
dfs1(1);
for(int i=1;i<=n;i++){
dfs(i,fa[i],a[i],0);
cout<<ans[i]-1<<' ';
}
唉,真是太可惜了(加上这个函数我肯定就能冲比赛第一了),比赛时只拿了个鸭蛋 。
好了,这篇讲解还是比较短的,因为题目简单,虽然我们班一个同学都没做出来。
如果认为讲得好的话,记得点个关注,谢谢!