POJ 2104 K-th Number 主席树

本文介绍了一种数据结构——主席树的实现原理及其在解决区间查询问题中的应用。通过具体的代码示例,详细展示了如何利用可持久化线段树的思想进行区间数字频率统计,并解答特定区间内的第k小元素查询问题。
题意:

给出\(n(1 \leq n \leq 10^5)\)个数字和\(m(1 \leq m \leq 5000)\)个询问。
每次询问形式如下:

  • \(l \, r \, k\)表示询问区间\([l,r]\)从小到大排序后第\(k\)个数字的大小。
分析:

推荐这篇主席树的教程,里面的图片十分简洁明了,一图胜千言。
主席树的本质就是可持久化线段树,为了节省空间,相邻两棵树共用了相同的的区间节点(也就是数据不发生变换的区间)。
仅对于数据发生变化的区间新建节点来更新维护信息。

区间维护的信息是值域上数字出现的次数,所以我们先把输入数据离散化一下。
查询第\(k\)小的时候类似于\(BST\)中的名次树,先查询区间\([l,r]\)中数字的个数\(cnt\)\(m=\frac{l+r}{2}\)是区间的中点。

  • 如果\(cnt \geq k\),那么第\(k\)小的值一定在左区间\([l, m]\)中,递归求解左区间的第\(k\)
  • 否则,第\(k\)小的值一定在右区间\([m, r]\)中,递归求解右区间的第\(k-cnt\)
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int maxn = 100000 + 10;

struct Node
{
    int lch, rch, sum;
};

int sz;
Node T[maxn << 5];

int newnode() {
    T[sz].lch = T[sz].rch = T[sz].sum = 0;
    return sz++;
}

int build(int L, int R) {
    int rt = newnode();
    if(L == R) return rt;
    int M = (L + R ) / 2;
    T[rt].lch = build(L, M);
    T[rt].rch = build(M + 1, R);
    return rt;
}

int update(int pre, int L, int R, int p) {
    int rt = newnode();
    T[rt].lch = T[pre].lch;
    T[rt].rch = T[pre].rch;
    T[rt].sum = T[pre].sum + 1;
    if(L == R) return rt;
    int M = (L + R) / 2;
    if(p <= M) T[rt].lch = update(T[pre].lch, L, M, p);
    else T[rt].rch = update(T[pre].rch, M + 1, R, p);
    return rt;
}

int query(int s, int t, int L, int R, int k) {
    if(L == R) return L;
    int M = (L + R) / 2;
    int cnt = T[T[t].lch].sum - T[T[s].lch].sum;
    if(cnt >= k) return query(T[s].lch, T[t].lch, L, M, k);
    else return query(T[s].rch, T[t].rch, M+1, R, k - cnt);
}

int n, m;
int a[maxn], b[maxn], tot, root[maxn];

int main()
{
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i++) {
        scanf("%d", a + i);
        b[i] = a[i];
    }
    sort(b + 1, b + 1 + n);
    tot = unique(b + 1, b + 1 + n) - (b + 1);

    sz = 0;
    root[0] = build(1, tot);
    for(int i = 1; i <= n; i++) {
        int id = lower_bound(b + 1, b + 1 + tot, a[i]) - b;
        root[i] = update(root[i - 1], 1, tot, id);
    }

    while(m--) {
        int l, r, k; scanf("%d%d%d", &l, &r, &k);
        int ans = query(root[l - 1], root[r], 1, tot, k);
        printf("%d\n", b[ans]);
    }

    return 0;
}

转载于:https://www.cnblogs.com/AOQNRMGYXLMV/p/5279729.html

内容概要:本文系统介绍了算术优化算法(AOA)的基本原理、核心思想及Python实现方法,并通过图像分割的实际案例展示了其应用价值。AOA是一种基于种群的元启发式算法,其核心思想来源于四则运算,利用乘除运算进行全局勘探,加减运算进行局部开发,通过数学优化器加速函数(MOA)和数学优化概率(MOP)动态控制搜索过程,在全局探索与局部开发之间实现平衡。文章详细解析了算法的初始化、勘探与开发阶段的更新策略,并提供了完整的Python代码实现,结合Rastrigin函数进行测试验证。进一步地,以Flask框架搭建前后端分离系统,将AOA应用于图像分割任务,展示了其在实际工程中的可行性与高效性。最后,通过收敛速度、寻优精度等指标评估算法性能,并提出自适应参数调整、模型优化和并行计算等改进策略。; 适合人群:具备一定Python编程基础和优化算法基础知识的高校学生、科研人员及工程技术人员,尤其适合从事人工智能、图像处理、智能优化等领域的从业者;; 使用场景及目标:①理解元启发式算法的设计思想与实现机制;②掌握AOA在函数优化、图像分割等实际问题中的建模与求解方法;③学习如何将优化算法集成到Web系统中实现工程化应用;④为算法性能评估与改进提供实践参考; 阅读建议:建议读者结合代码逐行调试,深入理解算法流程中MOA与MOP的作用机制,尝试在不同测试函数上运行算法以观察性能差异,并可进一步扩展图像分割模块,引入更复杂的预处理或后处理技术以提升分割效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值