题目链接: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 */