[USACO17JAN]Promotion Counting晋升者计数

题目描述

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

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

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

输入输出格式

输入格式:

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

接下来的 NN 行包括奶牛们的能力指数 p(1) \cdots p(N)p(1)p(N). 保证所有数互不相同,在区间 1 \cdots 10^91109​​ 之间。

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

输出格式:

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

 

输入输出样例

输入样例#1:
5
804289384
846930887
681692778
714636916
957747794
1
1
2
3
输出样例#1:
2
0
1
0
0
树上的树状数组或线段树
维护按后序遍历树时比当前点值大的数量
但是遍历到一个点时,维护的数量可能包含了他的兄弟,祖先
所以求出每个点的答案之前,记录出值pre,在遍历子树,加入树状数组,再求出值ans[x]
ans[x]=ans[x]-pre
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 struct Messi
 7 {
 8     int next,to;
 9 } edge[200001];
10 int c[400001];
11 int n,num,head[100001],a[100001],id[100001],ans[100001],p[100001];
12 void add(int u,int v)
13 {
14     num++;
15     edge[num].next=head[u];
16     head[u]=num;
17     edge[num].to=v;
18 }
19 int binary(int x)
20 {
21     int l=1,r=n,mid;
22     while(l<=r)
23     {
24         mid=(l+r)>>1;
25         if(a[mid]==x)return mid;
26         if(a[mid]>x)r=mid-1;
27         else l=mid+1;
28     }
29     return -1;
30 }
31 int getsum(int rt,int l,int r,int L,int R)
32 {
33     if (l>=L&&r<=R)
34     {
35         return c[rt];
36     }
37      int mid=(l+r)/2;
38      int x=0;
39      if (L<=mid)
40       x+=getsum(rt*2,l,mid,L,R);
41       if (R>mid)
42       x+=getsum(rt*2+1,mid+1,r,L,R);
43     return x;
44 }
45 void update(int rt,int l,int r,int k)
46 {
47     if (l==r)
48     {
49         c[rt]=1;
50      return;
51     }
52      int mid=(l+r)/2;
53       if (k<=mid)
54        update(rt*2,l,mid,k);
55        else update(rt*2+1,mid+1,r,k);
56   c[rt]=c[rt*2]+c[rt*2+1];
57 }
58 void dfs(int x)
59 {int i;
60     int tmp=getsum(1,1,n,id[x]+1,n);
61     for (i=head[x];i;i=edge[i].next)
62     {
63         dfs(edge[i].to);
64     }
65      ans[x]=getsum(1,1,n,id[x]+1,n)-tmp;
66     update(1,1,n,id[x]);
67 }
68 int main()
69 {int i,x;
70     cin>>n;
71     for (i=1; i<=n; i++)
72     {
73         scanf("%d",&p[i]);
74         a[i]=p[i];
75     }
76     sort(a+1,a+n+1);
77     for (i=1; i<=n; i++)
78         id[i]=binary(p[i]);
79     for (i=2; i<=n; i++)
80     {
81         scanf("%d",&x);
82         add(x,i);
83     }
84      dfs(1);
85      for (i=1;i<=n;i++)
86      printf("%d\n",ans[i]);
87 }

 

转载于:https://www.cnblogs.com/Y-E-T-I/p/7365414.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,这是一道经典的单调栈问题。题目描述如下: 有 $n$ 个湖,第 $i$ 个湖有一个高度 $h_i$。现在要在这些湖之间挖一些沟渠,使得相邻的湖之间的高度差不超过 $d$。请问最少需要挖多少个沟渠。 这是一道单调栈的典型应用题。我们可以从左到右遍历湖的高度,同时使用一个单调栈来维护之前所有湖的高度。具体来说,我们维护一个单调递增的栈,栈中存储的是湖的下标。假设当前遍历到第 $i$ 个湖,我们需要在之前的湖中找到一个高度最接近 $h_i$ 且高度不超过 $h_i-d$ 的湖,然后从这个湖到第 $i$ 个湖之间挖一条沟渠。具体的实现可以参考下面的代码: ```c++ #include <cstdio> #include <stack> using namespace std; const int N = 100010; int n, d; int h[N]; stack<int> stk; int main() { scanf("%d%d", &n, &d); for (int i = 1; i <= n; i++) scanf("%d", &h[i]); int ans = 0; for (int i = 1; i <= n; i++) { while (!stk.empty() && h[stk.top()] <= h[i] - d) stk.pop(); if (!stk.empty()) ans++; stk.push(i); } printf("%d\n", ans); return 0; } ``` 这里的关键在于,当我们遍历到第 $i$ 个湖时,所有比 $h_i-d$ 小的湖都可以被舍弃,因为它们不可能成为第 $i$ 个湖的前驱。因此,我们可以不断地从栈顶弹出比 $h_i-d$ 小的湖,直到栈顶的湖高度大于 $h_i-d$,然后将 $i$ 入栈。这样,栈中存储的就是当前 $h_i$ 左边所有高度不超过 $h_i-d$ 的湖,栈顶元素就是最靠近 $h_i$ 且高度不超过 $h_i-d$ 的湖。如果栈不为空,说明找到了一个前驱湖,答案加一。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值