题目链接:https://cn.vjudge.net/problem/HDU-3887
题目大意
有一棵以p为根的树,找出每个点的子树中,编号比其小的数量。例如,9号节点的子树有2,8,10,11,比9小的有两个,那么输出2。
分析
先dfs序,把每个点及其子树放在一个连续的区间内。接下来,就是要求比每个节点编号小的数量了。我们可以从1号节点开始,即从最小的开始,先查询dfs后对应区间内有多少个,再更新,更新时放入的是in[i],不能放i,因为i不一定在i+1对应的区间内。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int N = 100010;
int n, p, cnt, tot;
int in[N], out[N], head[2*N], c[N];
struct Edge{
int to, nxt;
}edge[2*N];
int lowbit(int x)
{
return x & (-x);
}
void updata(int x)
{
while(x < N)
{
c[x] += 1;
x += lowbit(x);
}
}
int query(int x)
{
int sum = 0;
while(x)
{
sum += c[x];
x -= lowbit(x);
}
return sum;
}
void add(int a, int b)
{
edge[cnt].to = b;
edge[cnt].nxt = head[a];
head[a] = cnt++;
}
void dfs(int x, int pre)
{
in[x] = ++tot;
for(int i = head[x]; i != -1; i = edge[i].nxt)
{
int j = edge[i].to;
if(j == pre) continue;
dfs(j, x);
}
out[x] = tot;
}
int main()
{
while(~scanf("%d %d", &n, &p))
{
if(n == 0 && p == 0) break;
cnt = tot = 0;
memset(head, -1, sizeof head);
memset(c, 0, sizeof c);
for(int i = 1; i < n; i++)
{
int a, b;
scanf("%d %d", &a, &b);
add(a, b);
add(b, a);
}
dfs(p, 0);
for(int i = 1; i <= n; i++)
{
printf("%d%s", query(out[i]) - query(in[i] - 1), i == n ? "\n" : " ");
updata(in[i]);
}
}
return 0;
}