区间取min线段树的一种奇妙势能分析

这里写图片描述

比如这题
设势函数=每个点所管区间内互异值的个数总和
一开始势能最大nlogn
操作1使得势能最多增加logn
操作2我们这样处理:每个点记录一下最大值和次大值,若x大于最大值退掉,若x在最大值与次大值之间则打一个tag,若x小于次大值则递归下去。
可以发现,每次递归下去时势函数都至少减1,这时我们要花费向下走左右子树,共2的时间。

那么,总的时间复杂度就控制在了势函数最大值(n+m) log n上。
但是题解的分析是n log^2 n的,不知道这个奇妙的分析方法有没有问题。

2019 UPD

这种线段树叫吉司机线段树。
复杂度的确是 O ( ( n + m ) l o g n ) O((n + m) log n) O((n+m)logn)
假如有区间加操作,那么复杂度可以证明为 O ( n l o g 2 n ) O(n log^2 n) O(nlog2n)
但是实践上效率更接近于 O ( n l o g n ) O(n log n) O(nlogn),并没有能卡成log^2的数据(没准他本来就是一个log呢?)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个线段树区间最值的示例代码,以查询区间最小值为例: ```cpp #include <cstdio> #include <algorithm> using namespace std; const int MAXN = 100005; int n, m; int a[MAXN]; struct Node { int l, r, minn; } tr[MAXN*4]; // 建立线段树 void build(int u, int l, int r) { tr[u].l = l, tr[u].r = r; if (l == r) { // 叶子节点 tr[u].minn = a[l]; return; } int mid = (l + r) >> 1; build(u<<1, l, mid); build(u<<1|1, mid+1, r); tr[u].minn = min(tr[u<<1].minn, tr[u<<1|1].minn); } // 查询区间最小值 int query(int u, int ql, int qr) { if (ql <= tr[u].l && tr[u].r <= qr) { // 完全包含 return tr[u].minn; } int mid = (tr[u].l + tr[u].r) >> 1; int res = 0x3f3f3f3f; // 初始化为一个极大值 if (ql <= mid) res = min(res, query(u<<1, ql, qr)); // 左子区间有交集 if (qr > mid) res = min(res, query(u<<1|1, ql, qr)); // 右子区间有交集 return res; } int main() { scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); } build(1, 1, n); // 建立线段树 while (m--) { int l, r; scanf("%d%d", &l, &r); printf("%d\n", query(1, l, r)); } return 0; } ``` 这段代码中,建立线段树的过程采用递归方式实现,查询区间最小值的过程也采用递归方式实现。对于每个节点,维护该节点对应区间的最小值。查询区间最小值的过程,采用线段树的递归查询方式,将查询区间划分成若干个离散的单元,并依次查询每个单元,最终得到查询区间的最小值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值