线段树合并

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;

 

以上是关于线段树合并的主要内容,如果未能解决你的问题,请参考以下文章

[模板] 线段树合并

线段树合并

线段树合并

线段树合并

线段树合并

线段树合并