NKOJ-4240 人数统计

P4240 人数统计
问题描述

何老板经营着一家大公司, 公司总共有 n 个员工, 编号 1 到 n。 何老板编号 1。
除了何老板外, 每个员工有且仅有一个直接上司(呈树状)。 每个员工都有一个工作能力值。 何老板想知道, 对于第 i 号员工, 他的所有下属(包括间接的下属)中, 能力值比 i 强的有多少人? (即统计 i 为根的子树中, 能力值比 i 大的节点数)

输入格式

第一行, 一个整数 n, 表示公司职员的总数, 员工编号 1 到 n。
接下来 n 行, 每行一个整数, 其中第 i 行表示 i 号员工的能力值。
接下来 n-1 行, 每行一个整数, 其中第 i 行的整数表示编号 i+1 的员工的直接上司的编号。

输出格式

n 行, 每行一个整数, 其中第 i 行表示编号为 i 的员工的下属中, 能力比 i 强的人数。

输入输出样例
样例输入 1

5 8
0424
85697
66168
70466
95774
1 1 2 3

样例输出 1

2 0 1 0 0

样例说明 无
数据范围

对于约 30%的数据,有 2<=N<=200
对于约 100%的数据,有 2<=N<=100,000 1<=能力值<=1,000,000,000.

这能力值是怎么测出来的 严重怀疑公正性

题解

其实这道题目是个很暴力的深搜

其实很容易想到深搜统计能力值大于当前节点的节点个数
所以我们优化就在于统计上面

我们开一个数组cnt
cnt[i]=(当前的能力值==i的人的个数)
然后我们在每次求结果时只需要求cnt[i]>当前能力值的点即可

那么统计就很简单了 用树状数组

关键在于 怎么能够让所有结点共用一个cnt数组

讲讲思路

如果只用一个cnt就会出现 当前求出的和 包含了 别人家的儿子

样例数据
我们可以得到搜索顺序 1->2->4->3->5
我们的思路是 在跑完p的所有儿子之后 统计能力值大于p的能力值的点的个数

但是对于三号点 就会出现一种很尴尬的情况
三的cnt当中可能包含二的儿子的统计

那么这个时候我们操作就是

在跑p的儿子之前 先统计能力值大于p的能力值的点的个数
然后跑p的儿子
然后再一次统计能力值大于p的能力值的点的个数
前后之差即为 真·比爸爸强的儿子的个数
而不是 伪·比叔叔强的侄子的个数

但是能力值的范围太大了

我也不知道是夸这些员工强,还是夸这些员工能力强…

所以我们就要离散化

比如对于能力值 987654 654321 123456 224567
他们从大到小排序之后
    987654 654321 224567 123456

我们就记录 点的值排序后它的能力值的位置
点 位置 原能力值
1 1 987654
2 2 654321
3 4 123456
4 3 224567
那么在每次统计后 我们只需要在 点的位置上 +1 即可

然后由于我们是从大到小排序的
所以我们只需要用树状数组统计 位置小于 当前点位置 的位置 的值之和(很绕)

于是能力值被强行转成了[1,100000]之间的值
这样就不会爆空间了

注意

在重新编号(离散化)时 能力值相同的的点 应该标成 相同的位置

完成!!

附上对拍代码

//在OJ上要交要扩栈 所有注释的语句不是 扩栈 就是 输入输出
#include 
#include 
#include 
#define lowbit(i) (i&-i)
using namespace std;

struct company{int i,p,n;}a[100123];
bool cp1(company a,company b){return a.n>b.n;}
bool cp2(company a,company b){return a.i//const int main_stack=16;     
//char my_stack[128<<20];   
int n;
int bit[100123],cnt[100123];
int all=0,star[100123],nxt[100123],ent[100123];

void ADD(int s,int e)
{
    nxt[++all]=star[s];
    star[s]=all;
    ent[all]=e;
}

inline int input()
{
    char c=getchar();int o;
    while(c>57||c<48)c=getchar();
    for(o=0;c>47&&c<58;c=getchar())o=(o<<1)+(o<<3)+c-48;
    return o;
}

int add(int p){for(int i=p;i<=n;i+=lowbit(i))bit[i]++;}//树状数组点修改
int sum(int p)//树状数组的 统计
{
    if(p<=0)return 0;
    int ans=0;
    for(int i=p;i;i-=lowbit(i))ans+=bit[i];
    return ans;
}

int dfs(int s)//简单暴力的深搜
{
    int mark=sum(a[s].p-1);//第一次统计
    for(int bian=star[s],e=ent[bian];bian;bian=nxt[bian],e=ent[bian])dfs(e);//跑儿子节点
    cnt[s]=sum(a[s].p-1)-mark;//第二次统计并得出结果
    add(a[s].p);
}

int main()
{
//  freopen("count.in","r",stdin);
//  freopen("count.out","w",stdout);
//  __asm__("movl %%esp, (%%eax);\n"::"a"(my_stack):"memory");     
//  __asm__("movl %%eax, %%esp;\n"::"a"(my_stack+sizeof(my_stack)-main_stack):"%esp");     
    n=input();int s;
    for(int i=1;i<=n;i++)a[i].i=i,a[i].n=input();
    sort(a+1,a+n+1,cp1);
    a[0].n=1000000001;a[0].p=0;
    for(int i=1;i<=n;i++)a[i].p=(a[i].n==a[i-1].n)?a[i-1].p:a[i-1].p+1;
    for(int e=2;e<=n;e++)
    {
        s=input();
        ADD(s,e);
    }
    sort(a+1,a+n+1,cp2);
    dfs(1);
    for(int i=1;i<=n;i++)printf("%d\n",cnt[i]);
//  __asm__("movl (%%eax), %%esp;\n"::"a"(my_stack):"%esp");
}

你可能感兴趣的:(NKOI,搜索)