poj 2104 K-th Number (主席树入门模板题)

代码风格参考了这篇博客:http://www.cnblogs.com/elpsycongroo/p/7296139.html
理解主席树主要是这篇博客:http://www.cnblogs.com/elpsycongroo/p/7296139.html

摘抄了一段主席树的解释:所谓主席树呢,就是对原来的数列[1..n]的每一个前缀[1..i](1≤i≤n)建立一棵线段树,线段树的每一个节点存某个前缀[1..i]中属于区间[L..R]的数一共有多少个(比如根节点是[1..n],一共i个数,sum[root] = i;根节点的左儿子是[1..(L+R)/2],若不大于(L+R)/2的数有x个,那么sum[root.left] = x)。若要查找[i..j]中第k大数时,设某结点x,那么x.sum[j] - x.sum[i - 1]就是[i..j]中在结点x内的数字总数。而对每一个前缀都建一棵树,会MLE,观察到每个[1..i]和[1..i-1]只有一条路是不一样的,那么其他的结点只要用回前一棵树的结点即可,时空复杂度为O(nlogn)。

自己的代码丑陋,见谅

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 1e5 + 5;
struct node {
    int l, r, cnt;
    /*
    注意!!!
    l,r不是指区间,而是节点的序号
    cnt是有多少个数字出现
    */
} T[maxn * 20];
int len, n, m, a[maxn], t[maxn], root[maxn], tot;
// root 保存的是所有历史记录
int build(int l, int r) {
    int rt = ++tot;
    T[rt].l = 0;
    T[rt].r = 0;
    T[rt].cnt = 0;//建立一棵空树
    if(l == r) return rt;
    int mid = (l + r) >> 1;
    T[rt].l = build(l, mid);//左右递归建立
    T[rt].r = build(mid + 1, r);
    return rt;
}
int updata(int l, int r, int pre, int x) {
    int rt = ++tot;
    T[rt] = T[pre];//相当于连接新节点和以前节点的左右子树
    T[rt].cnt++;
    if(l == r) return rt;
    int mid = (l + r) >> 1;//每次二分添加一个新节点
    if(mid >= x)T[rt].l = updata(l, mid, T[pre].l, x);
    else T[rt].r = updata(mid + 1, r, T[pre].r, x);
    return rt;
}
int query(int l, int r, int pre, int rt, int k) {
    if(l == r) return l;
    int mid = (l + r) >> 1;
    int sum = T[T[rt].l].cnt - T[T[pre].l].cnt;//每次比较两个节点的左子树
    if(sum >= k) return query(l, mid, T[pre].l, T[rt].l, k);
    else return query(mid + 1, r, T[pre].r, T[rt].r, k - sum);//如果在右子树上别忘了减去sum
}
int main() {
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
        t[i] = a[i];
    }
    sort(t + 1, t + 1 + n);//必须离散化
    len = unique(t + 1, t + 1 + n) - t - 1;
    for(int i = 1; i <= n; i++) {
        a[i] = lower_bound(t + 1, t + 1 + len, a[i]) - t;
    }
    root[0] = build(1, n);
    for(int i = 1; i <= n; i++) {
        root[i] = updata(1, n, root[i - 1], a[i]);
    }
    for(int i = 1; i <= m; i++) {
        int u, v, w;
        scanf("%d%d%d", &u, &v, &w);
        printf("%d\n", t[query(1, n, root[u - 1], root[v], w)]);
    }
    return 0;
}
Python网络爬虫与推荐算法新闻推荐平台:网络爬虫:通过Python实现新浪新闻的爬取,可爬取新闻页面上的标、文本、图片、视频链接(保留排版) 推荐算法:权重衰减+标签推荐+区域推荐+热点推荐.zip项目工程资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松复刻,拿到资料包后可轻松复现出一样的项目,本人系统开发经验充足(全领域),有任何使用问欢迎随时与我联系,我会及时为您解惑,提供帮助。 【资源内容】:包含完整源码+工程文件+说明(如有)等。答辩评审平均分达到96分,放心下载使用!可轻松复现,设计报告也可借鉴此项目,该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的。 【提供帮助】:有任何使用问欢迎随时与我联系,我会及时解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 下载后请首先打开README文件(如有),项目工程可直接复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值