线段树合并
Posted downrainsun
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线段树合并相关的知识,希望对你有一定的参考价值。
https://blog.csdn.net/keydou/article/details/83691189
bzoj 4756
题目大意:给出一棵树(根为 1),每个点有点权,对于每个点,询问它子树中点权比它大的点的个数
其实可以把这道题当成线段树合并入门题来做
首先把点权离散化,把所有的点都先单独建一颗权值线段树
然后从根开始 dfs ,一边 dfs 一边合并线段树,不断合并上去,最后求答案就行了
#include<cstdio> #include<cstring> #include<algorithm> #define N 100005 using namespace std; int n,m,t,tot; int first[N],v[N],nxt[N]; int a[N],ans[N],val[N],root[N],size[N*40],lc[N*40],rc[N*40]; void add(int x,int y) t++; nxt[t]=first[x]; first[x]=t; v[t]=y; void build(int &root,int l,int r,int val) root=++tot; size[root]=1; if(l==r) return; int mid=(l+r)>>1; if(val<=mid) build(lc[root],l,mid,val); else build(rc[root],mid+1,r,val); int Merge(int x,int y) if(!x) return y; if(!y) return x; size[x]+=size[y]; lc[x]=Merge(lc[x],lc[y]); rc[x]=Merge(rc[x],rc[y]); return x; int calc(int root,int l,int r,int x,int y) if(l>=x&&r<=y) return size[root]; int ans=0,mid=(l+r)>>1; if(x<=mid) ans+=calc(lc[root],l,mid,x,y); if(y>mid) ans+=calc(rc[root],mid+1,r,x,y); return ans; void dfs(int x) int i,j; for(i=first[x];i;i=nxt[i]) j=v[i],dfs(j); root[x]=Merge(root[x],root[j]); ans[x]=calc(root[x],1,m,val[x]+1,m); int main() int x,i; scanf("%d",&n); for(i=1;i<=n;++i) scanf("%d",&a[i]),val[i]=a[i]; for(i=2;i<=n;++i) scanf("%d",&x),add(x,i); sort(a+1,a+n+1),m=unique(a+1,a+n+1)-(a+1); for(i=1;i<=n;++i) val[i]=lower_bound(a+1,a+m+1,val[i])-a; build(root[i],1,m,val[i]); dfs(1); for(i=1;i<=n;++i) printf("%d\n",ans[i]); return 0;
以上是关于线段树合并的主要内容,如果未能解决你的问题,请参考以下文章