题目描述:
武当派一共有 n人,门派内 n 人按照武功高低进行排名,武功最高的人排名第 1,次高的人排名第 2,... 武功最低的人排名第 n。现在我们用武功的排名来给每个人标号,除了祖师爷,每个人都有一个师父,每个人可能有多个徒弟。
我们知道,武当派人才辈出,连祖师爷的武功都只能排行到 p。也就是说徒弟的武功是可能超过师父的,所谓的青出于蓝胜于蓝。
请你帮忙计算每个人的所有子弟(包括徒弟的徒弟,徒弟的徒弟的徒弟....)中,有多少人的武功超过了他自己。
输入格式
输入第一行两个整数 n,p(1≤n≤100000,1≤p≤n)n, p(1 <= n <= 100000, 1 <= p <= n)n,p(1≤n≤100000,1≤p≤n)。
接下来 n−1n-1n−1 行,每行输入两个整数 u,v(1≤u,v≤n)u, v(1 <= u, v <= n)u,v(1≤u,v≤n),表示 u 和 v 之间存在师徒关系。
输出格式
输出一行 n 个整数,第 i 个整数表示武功排行为 i 的人的子弟有多少人超过了他。
行末不要输出多余的空格。
样例输入
10 5
5 3
5 8
3 4
3 1
2 1
6 7
8 7
9 8
8 10
样例输出
0 0 2 0 4 0 1 2 0 0
题解:
如下图所示,对一颗树进行dfs,先搜索自己,然后是所有孩子节点。(以二叉树为例)
节点旁边的编号表示搜索的时序,从根节点开始搜,第一次搜的是1节点,然后搜2、4、8、5.....(图上只标注了一部分)
对于节点2来说,其子节点为搜索时序3,4,5的节点。
所以dfs的时候,每搜索到一个节点时,使用数组l[]标记其搜索时序,将子节点搜索完后,r[]数组标记回溯时的时序,这样就可以判断每个节点子节点的搜索时序范围。比如对2来说,dfs时序为2,搜完子节点回溯时时序为5,所以3-5时序的就是其子节点。
题目要求每一个节点子节点比自己小的个数,所以就可以将节点从1-n遍历,每次计算区间l[i]-r[i]的区间和sum(sum表示区间内比当前节点小的节点数),l[i]-r[i]区间就确保一定是i节点的子节点,因为要统计的节点一定比i小,所以每次遍历一个节点时,就将其时序l[i]的地方加1,这样后面求区间和时如果包含了之前加1的部分,那就可以统计进去。。(说得有点绕,具体看代码)
#include <bits/stdc++.h>
#define MAXN 100005
using namespace std;
// 从1开始计数
int C[MAXN]; // 树状数组
int head[MAXN];// i号节点的第一个子节点在结构体数组的下标为head[i]
int l[MAXN],r[MAXN];// l数组:记录dfs搜到i点时的次序,r数组:记录dfs回溯回i点时的时序
int n,f;// 输入
int times;// 深度搜索的次序
struct Node {
int val,next;// 节点编号,下一个兄弟节点在node数组的下标
}node[MAXN];
int num;// node数组元素数目
// 取x最低位的1
int lowbit(int x) {
return x & -x;
}
// pos下标处增加delta
void update(int pos,int delta) {
for(int i = pos;i <= n;i += lowbit(i)) {
C[i] += delta;
}
}
// 区间查询,查询前x项和
int query(int x) {
int cnt = 0;
for(int i = x;i >= 1;i -= lowbit(i)) {
cnt += C[i];
}
return cnt;
}
// 往图里加点
void add(int u,int v) {
node[num].next = head[u];
node[num].val = v;
head[u] = num++;
}
// 深度优先搜索,先搜完孩子节点,再搜本节点
// cur:当前节点编号,father: 父节点编号(用来避免重复搜索)
void dfs(int cur,int father) {
l[cur] = ++times;
// 遍历所有子节点
for(int i = head[cur];i != -1;i = node[i].next) {
int son = node[i].val;// 子节点编号
// 深搜
if(son != father)
dfs(son,cur);
}
// 回溯时标记
r[cur] = times;
}
int main() {
scanf("%d%d",&n,&f);
for (int i = 0;i <= n;i++) head[i] = -1;// 初始化
int u,v;
for(int i = 1;i < n;i++) {
scanf("%d%d",&u,&v);// u是v的师父
add(u,v);
add(v,u);
}
dfs(f,-1);// 从根节点开始搜索
// 从第一名到最后一名遍历武林高手
for(int i = 1;i <= n;i++) {
printf("%d\n",query(r[i])-query(l[i]));
update(l[i],1);
}
return 0;
}