P3605 [USACO17JAN]Promotion Counting晋升者计数 (线段树合并)

问题描述:

奶牛们又一次试图创建一家创业公司,还是没有从过去的经验中吸取教训–牛是可怕的管理者!

为了方便,把奶牛从 1-n(n<=1e6) 编号,把公司组织成一棵树,1 号奶牛作为总裁(这棵树的根节点)。除了总裁以外的每头奶牛都有一个单独的上司(它在树上的 “双亲结点”)。所有的第 i 头牛都有一个不同的能力指数 p(i),描述了她对其工作的擅长程度。如果奶牛 i 是奶牛 j 的祖先节点(例如,上司的上司的上司),那么我们我们把奶牛 j 叫做 i 的下属。

不幸地是,奶牛们发现经常发生一个上司比她的一些下属能力低的情况,在这种情况下,上司应当考虑晋升她的一些下属。你的任务是帮助奶牛弄清楚这是什么时候发生的。简而言之,对于公司的中的每一头奶牛 i,请计算其下属 j 的数量满足 p(j) > p(i)。

输入格式

输入的第一行包括一个整数 N。

接下来的 N 行包括奶牛们的能力指数 p(1)⋯p(N). 保证所有数互不相同,在区间 1-1e9之间。

接下来的 N-1行描述了奶牛 2-N 的上司(双亲节点)的编号。再次提醒,1 号奶牛作为总裁,没有上司。

输出格式

输出包括 N 行。输出的第 i 行应当给出有多少奶牛 i 的下属比奶牛 i 能力高。

输入

5
804289384
846930887
681692778
714636916
957747794
1
1
2
3

输出 #1

2
0
1
0
0

分析:

比较简单的线段树合并

数据太大1e9,先离散化
假设当前节点为x,ans(x)等于x的所有子节点中大于p(x)的数的个数
将树x和子树合并,权值线段树可以直接求出大于p(x)的数的个数,得到ans(x)

code:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<cstring>
using namespace std;
const int maxm=1e5+5;
int lc[maxm*40],rc[maxm*40],r[maxm*40],a[maxm*40],cnt;
int xx[maxm],num;//离散化
int p[maxm];//点权
vector<int>g[maxm];//边
int res[maxm];
void build(int &k,int l,int r,int x){//建树
    k=++cnt;
    a[k]=1;
    if(l==r)return ;
    int mid=(l+r)/2;
    if(x<=mid)build(lc[k],l,mid,x);
    else build(rc[k],mid+1,r,x);
}
int emerged(int x,int y){
    if(!x||!y)return x+y;
    a[x]+=a[y];
    lc[x]=emerged(lc[x],lc[y]);
    rc[x]=emerged(rc[x],rc[y]);
    return x;
}
int ask(int st,int ed,int l,int r,int node){
    if(st<=l&&ed>=r){
        return a[node];
    }
    int mid=(l+r)/2;
    int ans=0;
    if(st<=mid)ans+=ask(st,ed,l,mid,lc[node]);
    if(ed>=mid+1)ans+=ask(st,ed,mid+1,r,rc[node]);
    return ans;
}
void dfs(int x){//dfs更新答案
    int len=g[x].size();
    for(int i=0;i<len;i++){
        int v=g[x][i];
        dfs(v);
        r[x]=emerged(r[x],r[v]);
    }
    res[x]=ask(p[x]+1,num,1,num,r[x]);
}
signed main(){
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&p[i]);
        xx[i]=p[i];
    }
    sort(xx+1,xx+1+n);
    num=unique(xx+1,xx+1+n)-xx-1;
    for(int i=1;i<=n;i++){
        p[i]=lower_bound(xx+1,xx+1+num,p[i])-xx;
        build(r[i],1,num,p[i]);
    }
    for(int i=2;i<=n;i++){
        int fa;
        scanf("%d",&fa);
        g[fa].push_back(i);
    }
    dfs(1);
    for(int i=1;i<=n;i++){
        printf("%d\n",res[i]);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值