P3605 [USACO17JAN]Promotion Counting P (线段树合并)

前言

线段树合并:https://www.luogu.com.cn/blog/styx-ferryman/xian-duan-shu-ge-bing-zong-ru-men-dao-fang-qi

题意

给你一颗\(n\)个节点的树,每个节点都有权值且唯一,输出\(n\)行,输出的第\(i\)行应当给出有多少节点\(i\)的孩子比节点\(i\)权值大。

思路

有很多做法,这里用线段树合并来解决。

#include<bits/stdc++.h>
using namespace std;
const int maxx = 1e5+10;
int a[maxx],b[maxx];
int t[30*maxx],ls[30*maxx],rs[30*maxx],rt[maxx],cnt;
int ans[maxx];
int head[maxx],to[maxx],ne[maxx],tot;
int mx;
void add(int u,int v)
{
    to[++tot]=v;ne[tot]=head[u];head[u]=tot;
}
void update(int &u,int l,int r,int x)
{
    if(!u)u=++cnt;
    if(l==r)
    {
        t[u]++;
        return;
    }
    int mid=(l+r)/2;
    if(x<=mid)update(ls[u],l,mid,x);
    else update(rs[u],mid+1,r,x);
    t[u]=t[ls[u]]+t[rs[u]];
}
int query(int u,int l,int r,int p,int q)
{
    if(!u)return 0;
    if(p<=l&&r<=q)return t[u];
    int mid=(l+r)/2;
    int ans=0;
    if(p<=mid)ans+=query(ls[u],l,mid,p,q);
    if(q>mid)ans+=query(rs[u],mid+1,r,p,q);
    return ans;
}
int merge(int a,int b,int l,int r)
{
    if(!a)return b;
    if(!b)return a;
    if(l==r)
    {
        t[a]+=t[b];
        return a;
    }
    int mid=(l+r)/2;
    ls[a]=merge(ls[a],ls[b],l,mid);
    rs[a]=merge(rs[a],rs[b],mid+1,r);
    t[a]=t[ls[a]]+t[rs[a]];
    return a;
}
void dfs(int u)
{
    for(int i=head[u];i;i=ne[i])
    {
        dfs(to[i]);
        rt[u]=merge(rt[u],rt[to[i]],1,mx);
    }
    ans[u]=query(rt[u],1,mx,min(a[u]+1,mx),mx);
    update(rt[u],1,mx,a[u]);
}
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]),b[i]=a[i];
    int x;
    for(int i=2;i<=n;i++)
        scanf("%d",&x),add(x,i);
    sort(b+1,b+1+n);
    mx=unique(b+1,b+1+n)-b-1;
    for(int i=1;i<=n;i++)
        a[i]=lower_bound(b+1,b+1+mx,a[i])-b;
    dfs(1);
    for(int i=1;i<=n;i++)
        printf("%d\n",ans[i]);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/HooYing/p/12961976.html
今日推荐