蓝桥杯模拟赛 青出于蓝而胜于蓝

武当派一共有 nn 人,门派内 nn 人按照武功高低进行排名,武功最高的人排名第 11,次高的人排名第 22,... 武功最低的人排名第 nn。现在我们用武功的排名来给每个人标号,除了祖师爷,每个人都有一个师父,每个人可能有多个徒弟。

我们知道,武当派人才辈出,连祖师爷的武功都只能排行到 pp。也就是说徒弟的武功是可能超过师父的,所谓的青出于蓝胜于蓝。

请你帮忙计算每个人的所有子弟(包括徒弟的徒弟,徒弟的徒弟的徒弟....)中,有多少人的武功超过了他自己。

输入格式

输入第一行两个整数 n, p(1 le n le 100000, 1 le p le n)n,p(1n100000,1pn)。

接下来 n-1n1 行,每行输入两个整数 u, v(1 le u, v le n)u,v(1u,vn),表示 uu 和 vv 之间存在师徒关系。

输出格式

输出一行 nn 个整数,第 ii 个整数表示武功排行为 ii 的人的子弟有多少人超过了他。

行末不要输出多余的空格。

样例输入

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序列+树状数组
利用dfs序列把树序列化,并可以用时间戳来维护一个非叶子节点的子树。对于一个区间 求这个区间内比某个值要小的值的个数,利用权值线段树的思想
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
const int maxn=100010;
int n,m;
vector<int> edge[maxn];
int in[maxn*2],out[maxn*2];
int ret;
int tree[maxn*2];
int lowbit(int t)
{
    return t&(-t);
}
void up(int x,int y)
{
    for(int i=x;i<=n;i+=lowbit(i))
        tree[i]+=y;
}
int getsum(int x)
{
    int ans=0;
    for(int i=x;i>0;i-=lowbit(i)) ans+=tree[i];
    return ans;
}
void init()
{
    memset(tree,0,sizeof(tree));
    ret=0;
    for(int i=0;i<=n;i++) edge[i].clear();
}
void dfs(int u,int fa)
{
    in[u]=++ret;
    int len=edge[u].size();
    for(int i=0;i<len;i++)
    {
        if(edge[u][i]!=fa)
        {
            dfs(edge[u][i],u);
        }
    }
    out[u]=ret;
}
int main()
{
    scanf("%d %d",&n,&m);
    init();
    for(int i=1;i<n;i++)
    {
        int x,y;
        scanf("%d %d",&x,&y);
        edge[x].push_back(y);
        edge[y].push_back(x);
    }
    dfs(m,m);
    for(int i=1;i<=n;i++)
    {
        cout<<getsum(out[i])-getsum(in[i]);
        if(i!=n) cout<<" ";
        up(in[i],1);
    }
    cout<<endl;
    return 0;
}
原文地址:https://www.cnblogs.com/z1141000271/p/8383688.html