【计蒜客】 青出于蓝胜于蓝-【dfs序+归并树 】

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

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

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

输入格式

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

接下来 n-1n−1 行,每行输入两个整数 u, v(1 \le u, v \le n)u,v(1≤u,v≤n),表示 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序列+归并树 (查询区间小于val的值有几个)
代码

#include
using namespace std;

const int N=100000+11;
const int inf = 0x3f3f3f3f;

int in[N],out[N],sz=0; int val[N];
vector<int>ve[N];
void dfs(int u,int pre){
    in[u]=++sz; val[sz]=u;
    for(int i=0;iint v=ve[u][i];
        if(v==pre) continue;
        dfs(v,u);
    }
    out[u]=sz;
}

vector<int>T[N<<2];
void Build(int rt,int le,int ri){
    if(le==ri ){
        T[rt].push_back(val[le]);
        return ;
    }
    int mid=(le+ri)>>1;
    Build(rt<<1,le,mid);
    Build(rt<<1|1,mid+1,ri);
    T[rt].resize(ri-le+1);
    merge(T[rt<<1].begin(),T[rt<<1].end(),T[rt<<1|1].begin(),T[rt<<1|1].end(),T[rt].begin());
}

int Query(int rt,int L, int R,int le,int ri,int vv){
    if(L==le&&ri==R)  return lower_bound(T[rt].begin(),T[rt].end(),vv)-T[rt].begin();
    int mid=(L+R)>>1;
    if(ri<=mid) return Query(rt<<1,L,mid,le,ri,vv);
    else if(le>mid) return Query(rt<<1|1,mid+1,R,le,ri,vv);
    else {
        return Query(rt<<1,L,mid,le,mid,vv)+Query(rt<<1|1,mid+1,R,mid+1,ri,vv);
    }
}

int rot;
int main(){
    int n; scanf("%d%d",&n,&rot);
    for(int i=1;iint a,b;scanf("%d%d",&a,&b);
        ve[a].push_back(b);
        ve[b].push_back(a);
    }
    dfs(rot,-1);
    Build(1,1,n);
    for(int i=1;i<=n;i++){
        if(i!=1) putchar(' ');
        int ans=Query(1,1,n,in[i],out[i],i);
        printf("%d",ans);
    }
return 0;
}

你可能感兴趣的:(线段树)