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

给一棵 N 个点的树,每个点有一个权值,求每个点的子树中有多少个点的权值比它大。

考虑线段树合并,将权值离散化,每个点开一棵权值线段树。

求答案时直接在权值线段树上查询,线段树合并时类似于可并堆。

要注意的是线段树要动态开点,合并时别忘了 up。

内存什么的最好算一下,数组别开小了。

 1 #include<bits/stdc++.h>
 2 #define rep(i,a,b) for(register int i=a;i<=b;++i)
 3 #define rpd(i,a,b) for(register int i=a;i>=b;--i)
 4 #define rep1(i,x) for(register int i=head[x];i;i=nxt[i])
 5 typedef long long ll;
 6 const int N=2e5+5;
 7 using namespace std;
 8 inline int read(){
 9     int x=0,f=1;char ch=getchar();
10     while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
11     while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
12     return x*f;
13 }
14 struct Tree{int x,l,r;}t[N<<4];
15 int n,tot,cnt;int a[N],b[N],head[N],nxt[N],to[N],rt[N],ans[N];
16 void add(int u,int v){nxt[++tot]=head[u];head[u]=tot;to[tot]=v;}
17 void up(int k){t[k].x=t[t[k].l].x+t[t[k].r].x;}
18 void build(int &k,int l,int r,int x){
19     k=++cnt;if(l==r){t[k].x=1;return;}
20     int mid=l+r>>1;
21     if(mid>=x)build(t[k].l,l,mid,x);
22     else build(t[k].r,mid+1,r,x);
23     up(k);
24 }
25 int find(int k,int l,int r,int L,int R){
26     if(L<=l&&r<=R)return t[k].x;
27     if(l>R||r<L)return 0;
28     int mid=l+r>>1;
29     return find(t[k].l,l,mid,L,R)+find(t[k].r,mid+1,r,L,R);
30 }
31 int merge(int x,int y){
32     if(!x||!y)return x+y;
33     t[x].x+=t[y].x;
34     t[x].l=merge(t[x].l,t[y].l);
35     t[x].r=merge(t[x].r,t[y].r);
36     up(x);
37     return x;
38 }
39 void dfs(int x){
40     rep1(i,x){
41         int p=to[i];dfs(p);
42         rt[x]=merge(rt[x],rt[p]);
43     }
44     int k=lower_bound(b+1,b+n+1,a[x])-b;
45     ans[x]=find(rt[x],1,n,k+1,n);
46 }
47 int main(){
48     n=read();rep(i,1,n)b[i]=a[i]=read();
49     sort(b+1,b+n+1);
50     rep(i,1,n){
51         int k=lower_bound(b+1,b+n+1,a[i])-b;
52         build(rt[i],1,n,k);
53     }
54     rep(i,2,n)add(read(),i);dfs(1);
55     rep(i,1,n)printf("%d\n",ans[i]);
56     return 0;
57 }
View Code

转载于:https://www.cnblogs.com/maximumhanyu/p/11433029.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值