题目链接

学习了一下主席树,感觉具体算法思路不大好讲。。

大概是先建个空线段树,然后类似于递推,每一个都在前一个“历史版本”的基础上建立一个新的“历史版本”,每个历史版本只需占用树高个空间(好神奇!)

查询时这道题是通过“历史版本”间作差解决

*另外提一下,在建立“历史版本”的过程中,是“新建”,而不是“更新”,是先copy过来原有的,再按个人需求改成自己的,所产生的一个新的“历史版本”

希望注释能有帮助

//吐槽一下,本人看kuangbin模板看得好费劲,一方面不习惯用lson[],rson[]数组搞线段树,另一方面他的update query函数都是用二分写的,看半天没理解。。。

以下是按个人习惯加了注释的代码

#include<cstdio>
#include<queue>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<cmath>
#define M 100000+5
using namespace std; const int N=1e5+;
const int INF=0x3f3f3f3f; struct node
{
int l,r; //l,r分别指向左右孩子,但由于历史的错乱,二者的值未必有规律
int size; //当前历史版本里,该节点中所包含的元素个数
//p.s. 在最后一个历史版本里,size即为总元素个数n
}tr[N*];
//主席树的初始状态为一“空架子”
//每次updata都在前一“历史版本”基础上新建h个结点,共updata n次,产生了n个历史版本,每个版本占用空间为h*sizeof(one node)
int tot; //结点建立顺序|所赋标号 int n; //共n个数
int a[N]; //读入数组
int root[N];//记录a[]中每一个数对应各“历史版本”在主席树中的根结点标号
int m; //t[]中元素个数
int t[N]; //离散数组
int pos(int x)
{
return lower_bound(t+,t++m,x)-t;
}
int q; //q个询问 int build(int l,int r)
{
int rt=tot++;
tr[rt].size=;
if(l==r) return rt;
int mid=(l+r)>>;
tr[rt].l=build(l,mid);
tr[rt].r=build(mid+,r);
return rt;
} // pre_node:最近一历史,[l,r]当前所更新到的包含x的区间
// x此次更新的“终点”
// 沿途要增加的“权值”
// p.s. 1<=l<=r<=m
int updata(int pre_node,int l,int r,int x,int v)
{
int rt=tot++; //rt为新结点的标号
tr[rt]=tr[pre_node]; //先拷贝原指向当前区间的结点
tr[rt].size+=v;
// 在函数不断向下深入过程中,更新node.size
if(l==r) return rt;
int mid=(l+r)>>;
if(x<=mid) tr[rt].l=updata(tr[pre_node].l,l,mid,x,v);
else tr[rt].r=updata(tr[pre_node].r,mid+,r,x,v);
return rt;
// 在函数逐步return过程中,更新node.l|r
} // ql,qr反应的是在a[]中的下标,对应着更新到该下标时的“历史版本”
// l,r反应的是在t[]中的下标
int query(int ql,int qr,int l,int r,int k)
{
if(l==r) return l;
int diff=tr[tr[qr].l].size-tr[tr[ql].l].size;//在更新到ql位置和更新到qr位置时两个“历史版本”下左孩子的size之差
// p.s. size的大小反应较小数的多少
int mid=(l+r)>>;
if(diff>=k) return query(tr[ql].l,tr[qr].l,l,mid,k);
else return query(tr[ql].r,tr[qr].r,mid+,r,k-diff);
}
int main()
{
while(~scanf("%d%d",&n,&q))
{
for(int i=;i<=n;i++)
scanf("%d",&a[i]),t[i]=a[i];
sort(t+,t++n);
m=unique(t+,t++n)-t-;
m++,t[m]=INF;
tot=;
root[]=build(,m);    //初始化一个空线段树
for(int i=;i<=n;i++)
{
int order=pos(a[i]);
root[i]=updata(root[i-],,m,order,);
//每次在上一次已完成的基础上新增h个结点
//从根 逐步新建结点到 a[i]在线段树上所对应的位置
//在这个逐步新建结点的过程中,要新建的结点的l,r始终满足l<=m<=r
//这些结点每一个的size都在pre_node的基础上+1
}
while(q--)
{
int l,r,k;
scanf("%d%d%d",&l,&r,&k);
printf("%d\n",t[query(root[l-],root[r],,m,k)]);
}
}
}

poj_2104: K-th Number 【主席树】的更多相关文章

  1. poj2104 k-th number 主席树入门讲解

    poj2104 k-th number 主席树入门讲解 定义:主席树是一种可持久化的线段树 又叫函数式线段树   刚开始学是不是觉得很蒙逼啊 其实我也是 主席树说简单了 就是 保留你每一步操作完成之后 ...

  2. poj 2104 K-th Number 主席树+超级详细解释

    poj 2104 K-th Number 主席树+超级详细解释 传送门:K-th Number 题目大意:给出一段数列,让你求[L,R]区间内第几大的数字! 在这里先介绍一下主席树! 如果想了解什么是 ...

  3. POJ 2104 K-th Number 主席树(区间第k大)

    题目链接: http://poj.org/problem?id=2104 K-th Number Time Limit: 20000MSMemory Limit: 65536K 问题描述 You ar ...

  4. POJ 2104 K-th Number ( 求取区间 K 大值 || 主席树 || 离线线段树)

    题意 : 给出一个含有 N 个数的序列,然后有 M 次问询,每次问询包含 ( L, R, K ) 要求你给出 L 到 R 这个区间的第 K 大是几 分析 : 求取区间 K 大值是个经典的问题,可以使用 ...

  5. poj2104 K-th Number区间第k小值 主席树

    原来主席树就是可持久化线段树啊,刚知道,,, 作为一道裸题,还是必A的,然而一开始偷懒不写离散化跪了N多遍,后来在缪大的帮助下发现了这个问题,遂A之 ——又是这种破问题,实在不想说自己了 把n个数看成 ...

  6. POJ2104 K-th Number[主席树]【学习笔记】

    K-th Number Time Limit: 20000MS   Memory Limit: 65536K Total Submissions: 51440   Accepted: 17594 Ca ...

  7. [poj2104] K-th Number (主席树)

    主席树 Description You are working for Macrohard company in data structures department. After failing y ...

  8. poj 2104 K-th Number(主席树 视频)

    K-th Number 题意: 给你一些数,让你求一个区间内,第k大的数是多少. 题解: 主席树第一题,看的qsc视频写的,戳戳戳 学到了unique函数,他的作用是:把相邻的重复的放到后面,返回值是 ...

  9. 主席树:POJ2104 K-th Number (主席树模板题)

    K-th Number Time Limit: 20000MS   Memory Limit: 65536K Total Submissions: 44952   Accepted: 14951 Ca ...

  10. 【POJ2104】【HDU2665】K-th Number 主席树

    [POJ2104][HDU2665]K-th Number Description You are working for Macrohard company in data structures d ...

随机推荐

  1. Java IO流之打印流与标准流

    一.打印流 1.1打印流特点与构造方法 1)PrintStream和PrintWriter类都提供了一系列重载的print和println方法来输出各种类型的数据. 2)PrintStream和Pri ...

  2. cpp(第三章)

    1.使用{}初始化时,{}若为空则默认初始化为0,至于防范类型转化错误 2.int对计算机而言最为自然的长度,处理起来效率最高的长度.int可能是short(16位)也可能是long(32位),在知道 ...

  3. java入门学习笔记之1(类的定义,代码的编译执行)

    这篇文章讲解Java代码的基本执行过程 我们先抛开各种JAVA IDE,开发工具,只使用文本编辑器,以突出最本质的东西. 在Linux环境下,我们编辑一个文件: vim HelloWorld.java ...

  4. while和do while循环的简要概述

    循环结构 1.while结构 在英文中"while"这个词的意思是"当",而在 Java 程序设计中,也可以将其理解为"当",其语法结构是: ...

  5. android开发中关于继承activity类中方法的调用

    android开发中关于继承activity类中的函数,不能在其他类中调用其方法. MainActivity.java package com.example.testmain; import and ...

  6. 原生js实现Ajax的原理。

    Ajax(Asynchronous JavaScript and XML)表示异步的js与xml. 有别于传统web的同步开发方式. 原理:通过XMLHttpRequest对象向服务器发送异步请求,从 ...

  7. Latex: 插入数学公式

    write equations align equations to left To only align one equation, you can \begin{flalign} &\te ...

  8. 数独小算法,测试通过(Java)

    class SudokuMatrix { private int[][] matrix = new int[][] { {0, 5, 0, 6, 0, 1, 0, 0, 0}, {0, 0, 7, 0 ...

  9. 从Owin到System.Web.Http.Owin的HttpMessageHandlerAdapter看适配器模式

    .mytitle { background: #2B6695; color: white; font-family: "微软雅黑", "宋体", "黑 ...

  10. linux 权限字母含义

    查看某一文件夹下所有文件夹的权限情况:ls -l分别是:所有者(user)-所有者(user)-其他人(other)r 表示文件可以被读(read)w 表示文件可以被写(write)x 表示文件可以被 ...