dfs序+线段树--青出于蓝胜于蓝

https://nanti.jisuanke.com/t/20690

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

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

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

输入格式

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

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

输出格式

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

通过求dfs序,将子树上的问题转化为序列上的问题,然后用线段树求小于某个数的数的个数。

#include

#include

#include

#include

using namespace std;


#define lc (rt << 1)

#define rc (rt << 1 | 1)

#define mid (l + r) / 2


const int maxn = 1e5 + 5;

vector<int> mp[maxn];

int f[maxn];

int te = 1;

int s[maxn],t[maxn];

int mmin[maxn << 2],mmax[maxn << 2];

int pre[maxn];

int prep = 1;

int ans[maxn];

int n,p;


void dfs(int p)

{

    s[p] = ++te;

    pre[prep ++] = p;

    for (int i = 0; i < mp[p].size(); i ++) {

        if(s[mp[p][i]] == 0) {

            dfs(mp[p][i]);

        }

        else f[p] = mp[p][i];

    }

    t[p] = te;

}

void init(int l,int r,int rt)

{

    if(l == r){

        mmin[rt] = mmax[rt] = pre[l];

        return;

    }

    init(l, mid, lc);

    init(mid + 1, r, rc);

    mmax[rt] = max(mmax[lc],mmax[rc]);

    mmin[rt] = min(mmin[lc],mmin[rc]);

}


int querymmin(int ql,int qr,int l,int r,int rt)

{

    if(ql <= l && r <= qr){

       return mmin[rt];

    }

    int ans = maxn;

    if(ql <= mid) ans = min(ans,querymmin(ql, qr, l, mid , lc));

    if(mid < qr) ans = min(ans,querymmin(ql, qr, mid + 1, r, rc));

    return ans;

}


int querymmax(int ql,int qr,int l,int r,int rt)

{

    if(ql <= l && r <= qr){

        return mmax[rt];

    }

    int ans = 0;

    if(ql <= mid) ans = max(ans,querymmax(ql, qr, l, mid , lc));

    if(mid < qr) ans = max(ans,querymmax(ql, qr, mid + 1, r, rc));

    return ans;

}

int querycnt(int ql,int qr,int l,int r,int rt,int x)//求区间中比x小的数的个数

{

    if(ql <= l && r <= qr){

        if(querymmax(ql, qr, l, r, rt) < x ) return r - l + 1;

        else {

            if(querymmin(ql, qr, l, r, rt) >= x)return 0;

            else return querycnt(ql, qr, l, mid, lc, x) + querycnt(ql, qr, mid + 1, r, rc, x);

        }

    }

    int ans = 0;

    if(ql <= mid){

         ans += querycnt(ql,qr,l,mid,lc,x);

    }

    if(mid < qr){

          ans += querycnt(ql,qr,mid + 1,r,rc,x);

    }

    return ans;

}


int main()

{

    int a,b;

    f[p] = -1;

    

    scanf("%d%d",&n,&p);

    

    for (int i = 0; i < n - 1; i ++) {

        scanf("%d%d",&a,&b);

        mp[a].push_back(b);

        mp[b].push_back(a);

    }

    dfs(p);

    init(1, n, 1);

  

    for (int i = 1; i <= n; i ++) {

        ans[pre[i]] = querycnt(s[pre[i]] - 1, t[pre[i]] - 1, 1, n,1 ,pre[i]);

       //  printf("l = %d ,r = %d ,less than %d ,cnt = %d\n",s[pre[i]] - 1, t[pre[i]] - 1,pre[i],ans[pre[i]]);

    }

    for (int i = 1; i<= n; i ++) {

        printf("%d",ans[i]);

        if(i == n) printf("\n");

        else printf(" ");

    }

    

    return 0;

}



你可能感兴趣的:(线段树&树状数组&RMQ,二叉树&树)