【模板】线段树合并

这是线段树的最后一个知识点啦,终于不用继续练码量超大的线段树了,因为笔者已经掌握了~

例题:n只奶牛构成了一个树形的公司,每个奶牛有一个能力值pi,1号奶牛为树根。
问对于每个奶牛来说,它的子树中有几个能力值比它大的。

solution:
做法是每个点开一棵线段树,插入这个结点上的数,根节点就为这个点的序号。如果能把一个结点的线段树的信息在合理的时间内全部插入(或者说「合并」)到它父亲的线段树中,就可以由下往上做一遍树上递推,这样每个结点的线段树都存储的是整棵子树的信息了。

先放代码吧:

int merge(int p,int q,int l,int r) { if(!p) return q; if(!q) return p; if(l==r) { t[p].sum+=t[q].sum; return p; } int mid=(l+r)>>1; t[p].lson=merge(t[p].lson,t[q].lson,l,mid); t[p].rson=merge(t[p].rson,t[q].rson,mid+1,r); // t[p].sum=t[t[p].lson].sum+t[t[p].rson].sum; t[p].sum+=t[q].sum; //they are the same return p; }

这个代码还是相当好理解的,无需赘述。下面来证明一下复杂度。(我想了一中午终于想出一个超简洁的证明 —— 本来以为会很复杂的)

从代码中可以看出合并两棵树的复杂度约等于这两棵树 重合 的结点数。假如初始所有的线段树点数总和为 N N N 。因为只要合并两个节点,节点总个数就会少掉一个,所以复杂度应该是 O ( N ) O ( N ) O(N) 的。

所以不是所有情况下的线段树合并都是 O ( n l o g ⁡ n ) O ( n log ⁡ n ) O(nlogn) 的。

一般情况下我们只会对每个节点开动态开一条链,所以 N = n l o g ⁡ n N = n log ⁡ n N=nlogn ,复杂度才会是 O ( n l o g ⁡ n ) O ( n log ⁡ n ) O(nlogn)

哇怎么这么短就写完了 ……

update:「abc183F」Confluence
题意:n个人,每个人属于一个班级ci,这些人会有些小团体(并查集)

两种操作:

1 a b,将a所在的集体和b所在的集体合并

2 x y,问在x的集体中有多少人在y班

第一种做法启发式合并,每次将点数较少的接到点数较多的上面。具体可以用 m a p map map之类的 S T L STL STL来存集合。

第二种方法是线段树合并。我们把一个点所含的种类 [ 1 , 2 e 5 ] [1,2e5] [1,2e5] 看成一个树的结构,时间复杂度是严格 O ( n l o g n ) O(nlogn) O(nlogn) 的。

这个想法很妙。我们把一个线性的单点合并放到动态开点线段树上,这样每次合并一个点都删除了一个点,保证了算法的高效。

第二种算法的时间复杂度显然是优于第一种的,第一种做法只能“单个搬运”,导致合并代价是不稳定的。而第二种则把若干子树进行“整体搬运”,我们也通过理论证明了它时间上界和优秀的搬运性质(我们发现它在整个合并过程中没有新建点)。


__EOF__

本文作者仰望星空的蚂蚁
本文链接https://www.cnblogs.com/cqbzly/p/17530374.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   仰望星空的蚂蚁  阅读(8)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
历史上的今天:
2021-07-28 【题解】CF1550E Stringforces
2021-07-28 【题解】CF1550F Jumping Around
点击右上角即可分享
微信分享提示