bzoj 4756 [Usaco2017 Jan]Promotion Counting——线段树合并

本文深入探讨了线段树合并算法的实现细节,通过一个具体题目实例,讲解了如何使用线段树进行区间合并操作,包括节点的合并、查询和插入等关键步骤。文章提供了完整的C++代码示例,并解释了代码中的主要函数和数据结构。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4756

线段树合并裸题。那种返回 int 的与传引用的 merge 都能过。不知别的题是不是这样。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e5+5,M=N*17*5;
int n,m,a[N],tp[N],rt[N],hd[N],xnt,to[N],nxt[N],ans[N];
int tot,ls[M],rs[M],siz[M];
int rdn()
{
  int ret=0;bool fx=1;char ch=getchar();
  while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();}
  while(ch>='0'&&ch<='9')ret=(ret<<3)+(ret<<1)+ch-'0',ch=getchar();
  return fx?ret:-ret;
}
void add(int x,int y)
{
  to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;
}

void merge(int &x,int y)
{
  if(!x){x=y;return;}
  siz[x]+=siz[y];
  if(ls[y])merge(ls[x],ls[y]);
  if(rs[y])merge(rs[x],rs[y]);
}
/*
int merge(int x,int y)
{
  if(!x||!y)return x+y;
  int d=++tot;
  siz[d]=siz[x]+siz[y];
  ls[d]=merge(ls[x],ls[y]);
  rs[d]=merge(rs[x],rs[y]);
  return d;
}
*/
int query(int l,int r,int cr,int L,int R)
{
  if(!cr)return 0;if(l>=L&&r<=R)return siz[cr];
  int mid=l+r>>1,ret=0;
  if(mid>=L)ret=query(l,mid,ls[cr],L,R);
  if(mid<R)ret+=query(mid+1,r,rs[cr],L,R);
  return ret;
}
void insert(int l,int r,int &cr,int p)
{
  if(!cr)cr=++tot; siz[cr]++;
  if(l==r)return; int mid=l+r>>1;
  if(mid>=p) insert(l,mid,ls[cr],p);
  else insert(mid+1,r,rs[cr],p);
}
void dfs(int cr)
{
  for(int i=hd[cr],v;i;i=nxt[i])
    dfs(v=to[i]),/*rt[cr]=*/merge(rt[cr],rt[v]);
  ans[cr]=query(1,m,rt[cr],a[cr],m);
  insert(1,m,rt[cr],a[cr]);
}
int main()
{
  n=rdn(); for(int i=1;i<=n;i++)a[i]=tp[i]=rdn();
  sort(tp+1,tp+n+1); m=unique(tp+1,tp+n+1)-tp-1;
  for(int i=1;i<=n;i++)a[i]=lower_bound(tp+1,tp+n+1,a[i])-tp;
  for(int i=2,d;i<=n;i++)
    {
      d=rdn(); add(d,i);
    }
  dfs(1);
  for(int i=1;i<=n;i++)printf("%d\n",ans[i]);
  return 0;
}

 

转载于:https://www.cnblogs.com/Narh/p/9803966.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值