为了更好的阅读体检,可以查看我的算法学习博客树上计数
题目内容
给一棵 N N N 个点的有根树,所有点从 1 1 1 到 N N N 标号,且以 1 1 1 号点为根。问树上有多少个点满足其子树内(包含该点本身)的节点数大于等于 L L L 且小于等于 R R R 。
输入描述
输入的第一行包含三个正整数 N N N , L L L , R R R ,保证 N ≤ 1 0 5 , L ≤ R ≤ N N\le 10^5 , L \le R \le N N≤105,L≤R≤N 。
接下来的 N 一 1 N一1 N一1 行,第 i i i 行包含一个正整数 f i + 1 f_{i+1} fi+1 表示点 i + 1 i + 1 i+1 的父亲节点编号。
输入保证合法。
输出描述
输出一个正整数,表示对应的答案。
# 样例
输入
7 2 4
3
1
1
3
4
6
输出
3
题目思路
1.
d
f
s
dfs
dfs预处理出
d
p
i
dp_i
dpi 代表以点
i
i
i为根的子树的点的个数.容易发现转移方程
d
p
i
=
∑
j
是
i
的儿子
d
p
j
+
1
\large dp_i = \sum_{j是i的儿子} dp_j + 1
dpi=j是i的儿子∑dpj+1
2.挨个判断每个节点
d
p
i
∈
[
l
,
r
]
dp_i \in[l,r]
dpi∈[l,r]
代码+解析
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 6;
// e为邻接矩阵
vector<int> e[maxn];
int n , l , r;
// dp 定义如上
int dp[maxn];
void dfs (int u){
// 初始化dp[u] 含有u本身一个点
dp[u] = 1;
for (auto v : e[u]){
// 1.递归的求解儿子的dp值
dfs(v);
// 2.转移到自己
dp[u] += dp[v];
}
}
int main (){
// 关闭同步流,加速读入的速度
ios::sync_with_stdio(false);
cin >> n >> l >> r;
// 读入邻接矩阵
for (int i = 2 ; i <= n ; i++){
int x;
cin >> x;
e[x].push_back(i);
}
// 从1号点开始dfs求解
dfs(1);
int cnt = 0;
// 判断
for (int i = 1 ; i <= n ; i++){
if (l <= dp[i] && dp[i] <= r) cnt++;
}
cout << cnt << endl;
return 0;
}