0x01 前言

想法源于一道你谷的毒瘤题目。

这个方面的知识点好像挺新颖的。

于是和 JC 一起想出了该命题的 \(O(n)\) 解法。


0x02 算法本身

总所周知,线段树上的节点都对应表示的原序列里的一些结点。

而我们现在需要解决的问题就是:在极快的时间复杂度内求到每个原序列里的元素对应的元区间在线段树中的深度。

也就是求每个叶子节点的深度。

用线段树建树的朴素做法显然是:\(O(nlogn)\)。

但有些题目会比较恶心,于是我们考虑一种新的做法。

首先明确线段树的一个性质,如果树上有两个节点,且这两个结点表示区间长度相同,则处于相对位置相同的两个分别在这两个节点表示的区间中的原序列中的元素表示的元区间分别到这两个节点的距离相等。(好抽象 www。

于是我们将其剥离出来。

即有两个结点 \(p,q\),其中 \(p\) 表示区间 \(A\),\(q\) 表示区间 \(B\),且区间 \(A\) 的右端点为 \(A_l\),区间 \(B\) 的右端点为 \(B_l\),且记 \(f(x)\) 表示 \(x\) 为当前所在区间的第几个元素。

则对于任意两点 \(m,n\) ,\(m \in A,n \in B, f(m) = f(n)\),一定有 \(p\) 到表示 \(m\) 的元区间的叶子节点的距离等于一定有 \(q\) 到表示 \(n\) 的元区间的叶子节点的距离。

这其实很显然吧。。因为对于每个表示区间长度的节点,我们线段树往下划分的方式是不变的。

接下来,我们记 \(dep(x)\) 表示 \(x\) 这个元区间到根节点的距离,即表示 \(x\) 这个元区间的叶子节点的深度。\(Dep(x)\) 表示 \(x\) 这个节点的深度。

那么如果我们现在遍历到了一个节点 \(Q\),它表示的区间长度为 \(len\),而我们之前也遍历过一个表示区间长度为 \(len\) 的节点 \(P\),则定会有 \(dep(x) = dep(y) - Dep(P) + Dep(Q) (x \in Q,y \in P, f(x) = f(y))\)。

这是因为我们有刚刚那个性质嘛,\(x\) 这个元区间对应的叶子节点的深度可以分解为这个节点到 \(Q\) 的距离和 \(Q\) 的深度。因为 \(y\) 的深度也可以同样分解,所以前者就等于 \(dep(y) - Dep(p)\)。

那么我们可以利用一个 dfs,遍历线段树上的节点,如果遇到一个节点且之前遇到过表示区间长度相同的节点,则我们可以直接用之前那个点对当前节点表示区间内的所有元素进行深度转移,然后这个分支就可以结束了。

因为有记忆化,且你会发现每个节点我们只会更新一次,于是这就是个类 \(O(n)\) 算法。


0x03 部分实现

// q 是一个结构体。
// flag 表示之前是否访问过。
// l 表示上一个访问过的表示区间的长度和当前的一样的节点表示的区间的左端点。
// x 表示上一个访问过的表示区间的长度和当前的一样的节点的深度。
// 按照刚刚推的式子模拟即可。
void Get_Dep(int l, int r, int cnt) {
if(q[r - l + 1].flag) {
for(int i = l; i <= r; i++)
dep[i] = dep[i - l + q[r - l + 1].l] - q[r - l + 1].x + cnt;
return ;
}
if(l == r) {
dep[l] = cnt;
return ;
}
int mid = (l + r) >> 1;
Get_Dep(l, mid, cnt + 1);
Get_Dep(mid + 1, r, cnt + 1);
q[r - l + 1].flag = true;
q[r - l + 1].l = l;
q[r - l + 1].x = cnt;
}

0x04 应用场景

题目来源

这道题就可以用我们的思路进行预处理。

首先此题是求在线段树中从根到某一叶子节点经过路径权值和的期望。

朴素期望公式:一颗维护区间和的线段树,答案为每个节点表示的权值乘上每个节点的深度,然后在将它们全部加起来。

于是我们将每个节点的权值再返回到原序列中。

设原序列中元素 \(x\) 表示的元区间的深度为 \(g(x)\),其表示的数为 \(v(x)\)。

则原序列的每个元素会对答案产生的贡献为:\(v(x) \times \sum_{i = 1}^{g(x)} 2^i\)。

很显然当前这个元素在线段树种,会在其元区间到根的每一个节点表示的区间里出现。

其中 \(\sum_{i = 1}^{g(x)} 2^i\) 显然可以用上述算法预处理出来。

那么考虑区修。设所改区间为 \([l, r]\)。增加量为 \(x\)。

则这次修改对答案产生的贡献就是 \(Δ \times \sum_{x = l}^r\sum_{i = 1}^{g(x)} 2^i\)。

那么再维护一个 \(\sum_{i = 1}^{g(x)} 2^i\) 的前缀和不就结了吗?

(注,此题若用 double 会错掉一个点,可能与数据精度有关,建议直接使用 long long

#include <cstdio>

typedef long long LL;
int read() {
int k = 1, x = 0;
char s = getchar();
while (s < '0' || s > '9') {
if (s == '-')
k = -1;
s = getchar();
}
while (s >= '0' && s <= '9') {
x = (x << 3) + (x << 1) + s - '0';
s = getchar();
}
return x * k;
} const int MAXN = 1e6 + 5;
int a[MAXN], dep[MAXN];
LL w[MAXN], sum[MAXN]; struct node {
bool flag;
int l, x;
node() {}
node(bool Flag, int L, int X) {
flag = Flag;
l = L;
x = X;
}
} q[MAXN]; void Get_Dep(int l, int r, int cnt) {
if(q[r - l + 1].flag) {
for(int i = l; i <= r; i++)
dep[i] = dep[i - l + q[r - l + 1].l] - q[r - l + 1].x + cnt;
return ;
}
if(l == r) {
dep[l] = cnt;
return ;
}
int mid = (l + r) >> 1;
Get_Dep(l, mid, cnt + 1);
Get_Dep(mid + 1, r, cnt + 1);
q[r - l + 1].flag = true;
q[r - l + 1].l = l;
q[r - l + 1].x = cnt;
} int main() {
int n = read(), m = read(), qwq = read();
Get_Dep(1, n, 1);
w[1] = qwq;
for(int i = 2; i <= 23; i++)
w[i] = w[i - 1] + (qwq >> (i - 1));
for(int i = 1; i <= n; i++)
sum[i] = sum[i - 1] + w[dep[i]];
LL ans = 0;
for(int i = 1; i <= n; i++) {
a[i] = read();
ans += (a[i] * (sum[i] - sum[i - 1]));
}
for(int i = 1; i <= m; i++) {
int l = read(), r = read(), x = read();
ans += ((sum[r] - sum[l - 1]) * x);
printf("%lld\n", ans);
}
return 0;
}

Note -「序列元素在线段树上的深度」 感悟的更多相关文章

  1. LOJ 3059 「HNOI2019」序列——贪心与前后缀的思路+线段树上二分

    题目:https://loj.ac/problem/3059 一段 A 选一个 B 的话, B 是这段 A 的平均值.因为 \( \sum (A_i-B)^2 = \sum A_i^2 - 2*B \ ...

  2. CF 1405E Fixed Point Removal【线段树上二分】

    CF 1405E Fixed Point Removal[线段树上二分]  题意: 给定长度为\(n\)的序列\(A\),每次操作可以把\(A_i = i\)(即值等于其下标)的数删掉,然后剩下的数组 ...

  3. HDU 1087 Super Jumping! Jumping! Jumping!(求LSI序列元素的和,改一下LIS转移方程)

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1087 Super Jumping! Jumping! Jumping! Time Limit: 20 ...

  4. LibreOJ #6190. 序列查询(线段树+剪枝)

    莫队貌似是过不了的,这题是我没见过的科技... 首先区间按右端点排序,然后一个扫描线,扫到某个区间右端点时候计算答案,线段树上节点的信息并不需要明确定义,我们只要求线段树做到当前扫到now时,查询[L ...

  5. hdu 5930 GCD 线段树上二分/ 强行合并维护信息

    from NOIP2016模拟题28 题目大意 n个点的序列,权值\(<=10^6\) q个操作 1.单点修改 2.求所有区间gcd中,不同数个数 分析 1.以一个点为端点,向左或向右的gcd种 ...

  6. HDU 4747 Mex【线段树上二分+扫描线】

    [题意概述] 一个区间的Mex为这个区间没有出现过的最小自然数,现在给你一个序列,要求求出所有区间的Mex的和. [题解] 扫描线+线段树. 我们在线段树上维护从当前左端点开始的前缀Mex,显然从左到 ...

  7. [bzoj2962]序列操作_线段树_区间卷积

    序列操作 bzoj-2962 题目大意:给定一个n个数的正整数序列,m次操作.支持:1.区间加:2.区间取相反数:3.区间求选c个数的乘积和. 注释:$1\le n,m\le 5\cdot 10^4$ ...

  8. [NOIP2015模拟10.27] [JZOJ4270] 魔道研究 解题报告(动态开点+权值线段树上二分)

    Description “我希望能使用更多的魔法.不对,是预定能使用啦.最终我要被大家称呼为大魔法使.为此我决定不惜一切努力.”——<The Grimoire of Marisa>雾雨魔理 ...

  9. 【洛谷5537】【XR-3】系统设计(哈希_线段树上二分)

    我好像国赛以后就再也没有写过 OI 相关的博客 qwq Upd: 这篇博客是 NOIP (现在叫 CSP 了)之前写的,但是咕到 CSP 以后快一个月才发表 -- 我最近这么咕怎么办啊 -- 题目 洛 ...

随机推荐

  1. 【CSAPP】Shell Lab 实验笔记

    shlab这节是要求写个支持任务(job)功能的简易shell,主要考察了linux信号机制的相关内容.难度上如果熟读了<CSAPP>的"异常控制流"一章,应该是可以不 ...

  2. HashMap中红黑树插入节点的调整过程

    如果有对红黑树的定义及调整过程有过研究,其实很容易理解HashMap中的红黑树插入节点的调整过程. "红黑树定义及调整过程"的参考文章:<红黑树原理.查找效率.插入及变化规则 ...

  3. Keepalived入门学习

    一个执着于技术的公众号 Keepalived简介 Keepalived 是使用C语言编写的路由热备软件,该项目软件起初是专门为LVS负载均衡设计的,用来管理并监控LVS集群系统中各个服务节点的状态,后 ...

  4. [2021-TKK 暑期训练第一场] 1585:下馆子-3

    题目做了超链接 参考官方题解,作部分优化 下馆子 -3 题意: 给定n组数据,由name,time构成 当只有一个最大值时,输出该同学 当不止有一个最大值时,输出最先大于等于max次的同学 题解: 考 ...

  5. 『忘了再学』Shell基础 — 23、其他环境变量配置文件

    目录 1.注销时生效的环境变量配置文件 2.其他配置文件 3.Shell登录信息相关文件 (1)/etc/issue文件说明 (2)/etc/issue.net文件说明 (3)/etc/motd文件说 ...

  6. Lombok - 快速入门

    1. val 自动识别循环变量类型 本地变量和foreach循环可用. import java.util.ArrayList; import java.util.HashMap; import lom ...

  7. C++ 智能指针浅析

    C++ 智能指针浅析 为了解决 C++ 中内存管理这个老大难问题,C++ 11 中提供了三种可用的智能指针.(早期标准库中还存在一种 auto_ptr,但由于设计上的缺陷,已经被 unique_ptr ...

  8. LoRa无线传输技术与LoRaWAN无线模块的区别

    有不少人分不清LoRaWAN无线模块与LoRa网关无线传输技术到底有什么区别,他们在物联网领域的应用到底是什么样的. LoRaWAN指的是MAC层的组网协议,而LoRa是一个物理层的协议.虽然现有的L ...

  9. R数据分析:临床预测模型中校准曲线和DCA曲线的意义与做法

    之前给大家写过一个临床预测模型:R数据分析:跟随top期刊手把手教你做一个临床预测模型,里面其实都是比较基础的模型判别能力discrimination的一些指标,那么今天就再进一步,给大家分享一些和临 ...

  10. 我的开源之路:耗时 6 个月发布线程池框架,GitHub 1.7k Star!

    文章首发在公众号(龙台的技术笔记),之后同步到掘金和个人网站:xiaomage.info Hippo4J 线程池框架经过 6 个多月的版本迭代,2022 年春节当天成功发行了 1.0.0 RELEAS ...