树状数组

用 lowbit 把前缀和拆成 log n 段,支持 O(log n) 的单点改与区间查。

CSP-S

◎学完你会

1亲手走一遍 query(7)

求前缀和 query(7) 时,下标从 7 开始不断 减 lowbit:7 → 6 → 4。每次把该下标的树状数组值加起来。点按钮逐步看。

int res = 0; for(int i=7; i>0; i-=lowbit(i)) res += tr[i];
点左边按钮开始。

2关键命令

写法含义
lowbit(x)=x&-x取出 x 二进制里最低位的 1(如 lowbit(6)=2)
add(i, v)单点加:从 i 开始向上(i+=lowbit(i))更新所有覆盖区间
query(i)前缀和:从 i 开始向下(i-=lowbit(i))累加
query(r)-query(l-1)区间 [l, r] 的和
最大的坑:下标必须从 1 开始(0 的 lowbit 是 0,会死循环);update 是向上跳、query 是向下跳,方向写反结果全错。比赛里多用 int 而前缀和可能超 int,记得开 long long。

3区间改、单点查:套一层差分

要给 [l, r] 全部加 v、再问某个点的值,就对差分数组 d[l]+=v; d[r+1]-=v;,用树状数组维护 d 的前缀和即可。本质是「单点改」和「区间查」互换。

?跨学科:lowbit 就像「二进制里的个位数」

lowbit 只保留最低位的 1,好比十进制里只看个位——6 的个位是 6、4 的个位是 4,查询时每次「退到个位为 0 的下一档」。这和银行「整元整角」记账、或电脑里按 2 的幂分块管理内存是一回事:用结构化的分组,把 O(n) 的工作摊薄成 O(log n)。

✎练一练

lowbit(6) 的值为多少?
6 的二进制是 110,最低位 1 在第 2 位(值 2)。lowbit(6)=6&-6=2。

为什么 add 要向上(i+=lowbit)而 query 向下(i-=lowbit)?

tr[i] 存的是「一段区间」的和,这段区间恰好以 i 结尾、长度为 lowbit(i)。update 改一个点,所有包含该点的 tr 都要改,而包含它的区间的起点都在它右侧,所以向上跳;query 求前缀,从左往右把整段前缀拆成互不重叠的若干 tr 区间,所以向下跳。