c++之差分数组&最近公共祖先&树上差分

本文介绍了C++中差分数组的概念及其应用,通过实例解析了差分数组如何处理区间加法操作。接着,文章详细讨论了树上差分,涉及最近公共祖先(LCA)的计算,讲解了蜗牛和青蛙两种方法,并引入位操作优化求解LCA。最后,阐述了树上差分的实现,通过操作差分数组更新树上节点的权值。
摘要由CSDN通过智能技术生成

树上差分自己不算难

难的是天天爱跑步

先说差分数组。
这个玩意,就是差分数组里的每一个值等于原数组这个位置的值减去原数组这个位置前面的值,但差分数组第一个值等于原数组第一个值。
估计童鞋们看不懂(◎﹏◎)
为此,我特意准备了栗子<( ̄︶ ̄)↗[GO!]
原数组a: 9 3 5 2 7
差分数组b:9 -6 2 -3 5
所以除了第一个特例,b[i]=a[i]-a[i-1] QwQ

但是,,,差分数组除了麻烦就是麻烦?那这个玩意就流行不起来了,肯定还是有利处,请往下看↓。
一道题,有n次操作,每一次要在[ l[n] , r[n] ]的区间内每一个数+x,x也可以是负数。
这也忒简单了吧!遍历l,r区间不就得了!跟这有什么关系?(╬▔皿▔)凸¬_¬
抱歉,就怕时间复杂度超了

当n增多之后,可能就要超时,后果自负 QwQ
所以?差分数组正式登场也!
差分数组求这道题分为三大部分:

  1. 预处理:原数组->差分
  2. n次操作
  3. 后期处理:求前缀和(差分->原数组)
    预处理方法已经说了,重点探讨2. 3.
    n次操作,就先一次吧(多次 一讲着麻烦,二听不懂,三我很懒QwQ )
    以下l,r都指l[0],r[0],因为只有一次。
    方法就是b[l]+=x,b[r+1]-=x.
    或者去捡栗子(掉地了还能吃吗
    比如我们要在[1,3]区间+1
    那么9 -6 2 -3 5变成9 -5 2 -3 4
    (⊙_⊙)?
    Why?
    因为-6=3-9,但是目标里3加了一,相当于:3+1-9,当然结果也加1,相反过来结果加1,9没动,3也就理想上加了一;简单说被减数加一,结果加一。
    中间因为式两边都加所以差不变
    b[r+1]个呢?因为减数加一,所以结果减一,反之亦然。
    差分数组操作竟然这么简单!时间复杂度O(1)!!!( •̀ ω •́ )y
    别着急,还有一步,前缀和
    反正就是新的b推出新的a。
    b[i]=a[i]-a[i-1],那a[i]=b[i]+a[i-1]
    栗子是不是又掉地了?pickitup!
    如果i=1,那3=-6+9,而a[0]=b[0]=9,所以可以改成:a[1]=b[0]+b[1]
    如果i=2,那a[2]=b[2]+a[1],但a[1]=b[0]+b[1],所以a[2]=b[0]+b[1]+b[2]
    如果i=3,那a[3]=b[3]+a[2],但a[2]=b[0]+b[1]+b[2],所以a[3]=b[0]+b[1]+b[2]+b[3]
    发现了其中的规(mì)律(mì)了吗?
    所以! a i = ∑ k = 0 i b k a_i=\sum\limits_{k=0}^i b_k ai=k=0ibk
    推出后即完成此题。
    这个就是差分数组。

下面开始BB树上差分,
前面所有均是为以下做铺垫(是不是感觉还以为上面多重要,还细细的扣,结果落空╰(‵□′)╯?) 。。。请往下↓。
树上差分需要求LCA,即最近公共祖先。
我看的好多文章说LCA时都是一个链接过去,我只能进去看,看完了在回来看这个,我也懒得跑递归QwQ,正好也别让童鞋跑递归。。。2333
这里就详细介绍介绍
巨体思想 先把深度抬到一样高,然后一起往上 跳|爬 (借用了正则表达式符号d=====( ̄▽ ̄)b)
有两种方法
1.蜗牛型一步一步往上爬的暴力求解(一提到暴力,总是超时间的典型
2.青蛙型一步一步往上跳的倍增求解
蜗牛:没啥可说的,俩字:超时!
青蛙:大体思想(知道前面为什么用了吧 ):
移动速度要快!跳着走!
先预处理,
设数组 a n c i , j = i 往 上 走 2 j 个 anc_{i,j}=i往上走2^j个 anci,j=i2j, f a i = T h e F a t h e r O f I fa_i=The Father Of I fai=TheFatherOfI d e e p i = i 的 深 度 deep_i=i的深度 deepi=i
求出这些即可。

//基本转载:https://www.cnblogs.com/sllr15/p/5164996.html
int anc[1005][25];
int fa[1005];
vector <int > tree[1005];//tree[i]包含其子节点们
int deep[1005];

void dfs(int x)
{
   
    anc[x][0<
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值