題目鏈接:https://www.luogu.com.cn/problem/P3605
題目大意:給出一顆樹,每個點都有一個權值,最後對於每個點,輸出在它的子樹中,有多少個點的權值比它大。
把權值取負,就是p[j]<p[i]的節點個數。直接線段樹合併。
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int N = 1e5+5;
int w[N], p[N], lc[N*20], rc[N*20], rt[N*20], tot=0, n;
vector<int> v[N];
int tre[N*20], ans[N];//節點信息
void Insert(int &i, int l, int r, int x){//建樹
if(r<l) return;
i=++tot;//動態開點
if(l==r){
tre[i]++; return ;
}
int mid=(l+r)>>1;
if(x<=mid) Insert(lc[i], l, mid, x);
if(x>mid) Insert(rc[i], mid+1, r, x);
tre[i]=tre[lc[i]]+tre[rc[i]];//權值在區間[l, r]的元素個數
}
int Merge(int x, int y){//合併
if(!x) return y;
if(!y) return x;
lc[x]=Merge(lc[x], lc[y]);
rc[x]=Merge(rc[x], rc[y]);
tre[x]=tre[lc[x]]+tre[rc[x]];//節點信息合併
return x;
}
int query(int root, int l, int r, int x){//查詢子樹中權值<=x的節點個數
if(!root) return 0;
if(!x) return 0;
if(x==r) return tre[root];
int mid=(l+r)>>1;
if(x>mid) return tre[lc[root]]+query(rc[root], mid+1, r, x);
else return query(lc[root], l, mid, x);
}
void dfs(int u, int fa){
for(int i=0; i<v[u].size(); i++){
int to=v[u][i];
if(to!=fa){
dfs(to, u);
rt[u]=Merge(rt[u], rt[to]);//換根合併
}
}
ans[u]=query(rt[u], 1, n, w[u]-1);//查詢
}
int main(){
scanf("%d", &n);
for(int i=1; i<=n; i++){
scanf("%d", &w[i]);
w[i]=-w[i], p[i]=w[i];
}
sort(p+1, p+n+1);
int cut=unique(p+1, p+n+1)-p-1;
for(int i=1; i<=n; i++) w[i]=lower_bound(p+1, p+cut+1, w[i])-p;
for(int i=2; i<=n; i++){
int x; scanf("%d", &x);
v[i].push_back(x); v[x].push_back(i);
}
for(int i=1; i<=n; i++){
Insert(rt[i], 1, n, w[i]);//建樹
}
dfs(1, -1);
for(int i=1; i<=n; i++){
printf("%d\n", ans[i]);
}
return 0;
}
/*
5
804289384
846930887
681692778
714636916
957747794
1
1
2
3
2
0
1
0
0
*/