树状数组 P3605 [USACO17JAN]Promotion Counting晋升者计数

P3605 [USACO17JAN]Promotion Counting晋升者计数

题目描述

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

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

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

输入输出格式

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

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

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

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

一句话题意:给出一个树形结构,求每个节点其子树中节点权值比他大的个数。

如果树退化成链的话,那就是裸的求逆序对数,上树状数组。

接下来考虑树的情况,因为要求该节点与其子树中节点的大小关系,所以不难想到搞出一个DFS序,然后将DFS序看成序列,对上面的点每一次进行区间求和即可。

考虑我们现在要求节点i的答案,那么在树状数组里的一定是比i要大的,既然知道了这个顺序,那么就将每个节点按权值大小排序,每一次查询在DFS序里该点的dfn到dfn+size-1即可查询整个区间,用右端点减去左端点减一的位置的答案,即可得出此时的答案,再将现在这个点放进树状数组里,表示个数加一,去接着为权值更小的点的答案做贡献。

code:

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int wx=100017;
inline int read(){
    int sum=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-')f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        sum=(sum<<1)+(sum<<3)+ch-'0';
        ch=getchar();
    }
    return sum*f;
}
struct e{
    int nxt,to;
}edge[wx*2];
struct node{
    int v,id;
    friend bool operator < (const node& a,const node& b){
        return a.v>b.v;
    }
}a[wx];
int head[wx],size[wx],dfn[wx],ans[wx],sum[wx];
int n,num,tot,x;
void add(int from,int to){
    edge[++num].nxt=head[from];
    edge[num].to=to;
    head[from]=num;
}
void dfs(int u){
    dfn[u]=++tot;size[u]=1;
    for(int i=head[u];i;i=edge[i].nxt){
        int v=edge[i].to;
        dfs(v);
        size[u]+=size[v];
    } 
}
void update(int pos,long long k)
{
    for(int i=pos;i<=n;i+=(i&-i)){
        sum[i]+=k;
    }
}
int query(int x){
    long long re=0;
    for(int i=x;i>=1;i-=(i&-i)){
        re+=sum[i];
    }
    return re;
}
int main(){
    n=read();
    for(int i=1;i<=n;i++)a[i].v=read(),a[i].id=i;
    for(int i=2;i<=n;i++){
        x=read();add(x,i);
    }
    dfs(1);
    sort(a+1,a+1+n);
    for(int i=1;i<=n;i++){
        ans[a[i].id]=query(dfn[a[i].id]+size[a[i].id]-1)-query(dfn[a[i].id]-1);
        update(dfn[a[i].id],1);
    }
    for(int i=1;i<=n;i++)printf("%d\n",ans[i]);
    return 0;
}

转载于:https://www.cnblogs.com/wangxiaodai/p/9741495.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值