K-th Number

Input

The first line of the input file contains n --- the size of the array, and m --- the number of questions to answer (1 <= n <= 100 000, 1 <= m <= 5 000).
The second line contains n different integer numbers not exceeding 109 by their absolute values --- the array for which the answers should be given.

The following m lines contain question descriptions, each
description consists of three numbers: i, j, and k (1 <= i <= j
<= n, 1 <= k <= j - i + 1) and represents the question Q(i, j,
k).

Output

For each question output the answer to it --- the k-th number in sorted a[i...j] segment.
 
刚学了下划分树,写写理解;
如果有线段树与快排 的基础,很容易能理解划分树的;这里讲解得很好
思路:在tree[deep][i]中,下一层是对上一层的一个二叉排序,即左子树中没有大于tree[deep][mid]的,右子树中没有小于tree[deep][mid]的;但是等于的随便在左子树还是在右子树中;在快排中,不需要保存元素之间的前后顺序,所以直接双指针"补空位",但是在寻找第k小时,在同一颗子树中不能改变元素的顺序,所以在建树时,要预先处理出左子树中与[mid]值相等的数的个数即same值;之后直接从ls,rs;(实现细节详见代码)
在查询时,确实就像是线段树的区间查询,只不过这里通过预处理出来的区间[L,R]中每个数到左边界L,即区间[L,i]中在左子树的个数toleft,来缩小区间[left,right];
坑点:我还是使用输出外挂out()来输出结果,可是里面有负数。。。2333
14796K 813ms
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string.h>
#include<algorithm>
#include<map>
#include<queue>
#include<vector>
#include<cmath>
#include<stdlib.h>
#include<time.h>
#include<stack>
#include<set>
using namespace std;
#define rep0(i,l,r) for(int i = (l);i < (r);i++)
#define rep1(i,l,r) for(int i = (l);i <= (r);i++)
#define rep_0(i,r,l) for(int i = (r);i > (l);i--)
#define rep_1(i,r,l) for(int i = (r);i >= (l);i--)
#define MS0(a) memset(a,0,sizeof(a))
#define MS1(a) memset(a,-1,sizeof(a))
#define inf 0x3f3f3f3f
#define lson l, m, rt << 1
#define rson m+1, r, rt << 1|1
typedef __int64 ll;
template<typename T>
void read1(T &m)
{
T x=,f=;char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getchar();}
m = x*f;
}
template<typename T>
void read2(T &a,T &b){read1(a);read1(b);}
template<typename T>
void read3(T &a,T &b,T &c){read1(a);read1(b);read1(c);}
template<typename T>
void out(T a)
{
if(a>) out(a/);
putchar(a%+'');
}
const int N = 1e5+;
int sorted[N];
int tree[][N],toleft[][N];
void build(int l,int r,int deep)
{
if(l == r) return ;
int mid = (l + r) >> ;
int same = mid - l + ;
for(int i = l;i <= r;i++)//左子树中有same个和[mid]相等
if(tree[deep][i] < sorted[mid])
same--;
int ls = l,rs = mid + ;
for(int i = l;i <= r;i++){//按顺序将每一个上一层每一个数插入到下一层的左右子树中
int flag = ;
if(tree[deep][i] < sorted[mid] || (tree[deep][i] == sorted[mid] && same > )){
tree[deep+][ls++] = tree[deep][i];//模拟快排,只是要保持原来的顺序,才设置了ls rs
if(tree[deep][i] == sorted[mid]) same--;
}else{
tree[deep+][rs++] = tree[deep][i];
flag = ;
}
toleft[deep][i] = toleft[deep][i-] + flag; // 递推出[l,i]在左子树中的个数;
}
build(l,mid,deep+);
build(mid+,r,deep+);
}
int query(int left,int right,int k,int L,int R,int deep)
{
if(left == right) return tree[deep][left];
int mid = (L + R) >> ;
int x = toleft[deep][left-] - toleft[deep][L-];//[L,left-1]在左子树中的个数;
int y = toleft[deep][right] - toleft[deep][L-];//[L,right];
int cnt = y - x;//[left,right]
if(cnt >= k){
return query(L+x,L+y-,k,L,mid,deep+);//[L,L+x) 正好x个;
}else{//右孩子起点从mid + 1开始,由于减掉了mid所以
int rx = left - L - x;// [L,left)在右子树中的个数;
int ry = right - L - y;//**里面mid减去了(+1-1)
return query(mid + + rx,mid + + ry,k - cnt,mid + ,R,deep+);
}
}
int main()
{
int n,q;
read2(n,q);
rep1(i,,n){
read1(sorted[i]);
tree[][i] = sorted[i];
}
sort(sorted+,sorted+n+);
build(,n,);
while(q--){
int a,b,k;
read3(a,b,k);
printf("%d\n",query(a,b,k,,n,));
}
return ;
}

主席树还是建立在线段树之上,对于1...i段的数据是建立在第id[i]颗树中;但是直接每个都建完全线段树,空间复杂度为O(n^2);且系数约等于4;那么既然是按照顺序来建树的,为什么不建立在前面的基础之上呢?好,这样就构造出了第0颗树(空树),后面每个i都建立在id[i-1]之上。建立在前一颗线段树之上的具体含义又指的是什么呢(是什么数据要递推呢)?即只是建立一条从该点排序后应该在的叶子节点到根节点的路径,也就是条"树枝"。其余的分支还是用前一颗树的~~并且里面的递推关系很巧妙,即离散化之后,当当前插入的元素的最终位置是在当前节点的左子树时,生成一个左子树的节点(之后递归到左子树),并且这个左子树的节点所代表的值num[]确实要在前一个树的基础上加上当前的这个元素,即num[rt] + 1;这里就是实现对区间的处理;

时间和空间复杂度均为O(n*log(n))  25016K 1407MS

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string.h>
#include<algorithm>
#include<map>
#include<queue>
#include<vector>
#include<cmath>
#include<stdlib.h>
#include<time.h>
#include<stack>
#include<set>
using namespace std;
#define rep0(i,l,r) for(int i = (l);i < (r);i++)
#define rep1(i,l,r) for(int i = (l);i <= (r);i++)
#define rep_0(i,r,l) for(int i = (r);i > (l);i--)
#define rep_1(i,r,l) for(int i = (r);i >= (l);i--)
#define MS0(a) memset(a,0,sizeof(a))
#define MS1(a) memset(a,-1,sizeof(a))
#define inf 0x3f3f3f3f
typedef __int64 ll;
template<typename T>
void read1(T &m)
{
T x=,f=;char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getchar();}
m = x*f;
}
template<typename T>
void read2(T &a,T &b){read1(a);read1(b);}
template<typename T>
void read3(T &a,T &b,T &c){read1(a);read1(b);read1(c);}
template<typename T>
void out(T a)
{
if(a>) out(a/);
putchar(a%+'');
}
const int N = ;
const int M = N * ;
int val[N],sorted[N],m,tot;
int id[M],lson[M],rson[M],num[M];
int build(int l,int r)//第一颗空树根节点id[]标号为0
{
int newroot = tot++;
num[newroot] = ;//建立一颗空树
if(l == r) return newroot;
int mid = l + r >> ;
lson[newroot] = build(l,mid);
rson[newroot] = build(mid+,r);
return newroot;
}
int idx(int x)
{
return lower_bound(sorted+,sorted++m,x)-sorted;
}
int update(int pos,int add,int l,int r,int rt)
{
int newroot = tot++, t = newroot;
num[newroot] += num[rt] + add;//后一颗树是建立在前一颗树的基础之上
while(l < r){
int mid = l + r >> ;
if(pos <= mid){
lson[newroot] = tot++;
rson[newroot] = rson[rt];
newroot = lson[newroot];// 由于要更改左右子节点的num[],不好利用递归,而写成了循环结构
rt = lson[rt];//***对区间的递推
r = mid;
}else{
rson[newroot] = tot++;
lson[newroot] = lson[rt];
newroot = rson[newroot];
rt = rson[rt];
l = mid + ;
}
num[newroot] += num[rt] + add;
}
return t;
}
int query(int ida,int idb,int k,int L,int R)
{
if(L == R) return L;
int mid = L + R >> ,
cnt = num[lson[idb]] - num[lson[ida]];
if(cnt >= k){
return query(lson[ida],lson[idb],k,L,mid);
}else{
return query(rson[ida],rson[idb],k - cnt,mid + ,R);
}
}
int main()
{
int n,Q;
read2(n,Q);
rep1(i,,n){
read1(val[i]);
sorted[i] = val[i];
}
sort(sorted+,sorted++n);
m = unique(sorted+,sorted++n) - sorted - ;
tot = ;
id[] = build(,m);
rep1(i,,n){
int pos = idx(val[i]);
id[i] = update(pos,,,m,id[i-]);//在前一颗树id[i-1]的基础上建树(枝);
}
while(Q--){
int a,b,k;
read3(a,b,k);
printf("%d\n",sorted[query(id[a-],id[b],k,,m)]);
}
return ;
}

划分树与主席树的总结:

划分树只需要排好序,在后面每层模拟快排中,递推出每一个小区间到左边界在左子树中元素的个数即可;在之后的查询中,直接使用toleft(左区间到该点所在的左子树的个数)递归缩小区间,找到第K小即可;

主席树是线段树的递推,对1..i都建立在前一棵线段树基础之上,查询的时候,只是缩小k值,到只剩一个数时,就是第k小的数了;

 

poj 2104 K-th Number 划分树,主席树讲解的更多相关文章

  1. POJ 2104:K-th Number(主席树静态区间k大)

    题目大意:对于一个序列,每次询问区间[l,r]的第k大树. 分析: 主席树模板题 program kthtree; type point=record l,r,s:longint; end; var ...

  2. POJ 题目2761 Feed the dogs(主席树||划分树)

    Feed the dogs Time Limit: 6000MS   Memory Limit: 65536K Total Submissions: 16860   Accepted: 5273 De ...

  3. BZOJ_4026_dC Loves Number Theory _主席树+欧拉函数

    BZOJ_4026_dC Loves Number Theory _主席树+欧拉函数 Description  dC 在秒了BZOJ 上所有的数论题后,感觉萌萌哒,想出了这么一道水题,来拯救日益枯 竭 ...

  4. 线段树简单入门 (含普通线段树, zkw线段树, 主席树)

    线段树简单入门 递归版线段树 线段树的定义 线段树, 顾名思义, 就是每个节点表示一个区间. 线段树通常维护一些区间的值, 例如区间和. 比如, 上图 \([2, 5]\) 区间的和, 为以下区间的和 ...

  5. 【POJ 2104】 K-th Number 主席树模板题

    达神主席树讲解传送门:http://blog.csdn.net/dad3zz/article/details/50638026 2016-02-23:真的是模板题诶,主席树模板水过.今天新校网不好,没 ...

  6. [POJ2104] K – th Number (可持久化线段树 主席树)

    题目背景 这是个非常经典的主席树入门题--静态区间第K小 数据已经过加强,请使用主席树.同时请注意常数优化 题目描述 如题,给定N个正整数构成的序列,将对于指定的闭区间查询其区间内的第K小值. 输入输 ...

  7. POJ 2104:K-th Number(整体二分)

    http://poj.org/problem?id=2104 题意:给出n个数和m个询问求区间第K小. 思路:以前用主席树做过,这次学整体二分来做.整体二分在yr大佬的指点下,终于大概懂了点了.对于二 ...

  8. POJ2104 K-TH NUMBER 传说中的主席树

    题意:给出1e5个数,向你提5000问,L到R间的第K个数是多少? -------------------------------------------------------- 这个题着实没有其它 ...

  9. 学习笔记--函数式线段树(主席树)(动态维护第K极值(树状数组套主席树))

    函数式线段树..资瓷 区间第K极值查询 似乎不过似乎划分树的效率更优于它,但是如果主席树套树状数组后,可以处理动态的第K极值.即资瓷插入删除,划分树则不同- 那么原理也比较易懂: 建造一棵线段树(权值 ...

  10. 主席树总结(经典区间第k小问题)(主席树,线段树)

    接着上一篇总结--可持久化线段树来整理吧.点击进入 这两种数据结构确实有异曲同工之妙.结构是很相似的,但维护的主要内容并不相同,主席树的离散化.前缀和等思想也要更难理解一些. 闲话 话说刚学习主席树的 ...

随机推荐

  1. NPOI导出多表头Execl(通过html表格遍历表头)

    关于NPOI的相关信息,我想博客园已经有很多了,而且NPOI导出Execl的文章和例子也很多,但导出多表头缺蛮少的:今天要讲的通过自己画html表格:通过html表格来导出自定义的多表头: 先来看要实 ...

  2. Class File Editor: Source not found

    问题: 找不到需要的 *.java 文件就会报以下错误. 解决方案: 1. Window -> preferences -> Java -> Installed JREs 2. 选择 ...

  3. java.lang.NoSuchFieldError: RAW_XML_FILE_HEADER,调用XWPFTemplate动态合并生成一个新的docx文档时报错

    在使用 org.apache.poi 对office文件  根据表单内容和已上次的附件 动态合并成一个新的文档时,本地调试完全ok 但是发布倒Linux环境上就老是报这个错误java.lang.NoS ...

  4. 关于"干货集中营"的一个开源App

    中秋佳节,玩了一天,撸了两天代码,搞出这么个东东,共享出来,小伙伴们如果有兴趣,欢迎添砖加瓦. 数据接口为干货集中营的数据,接口地址:http://gank.io/api 使用到的技术清单如下: 1. ...

  5. 禁用 Browser Link,在浏览器调试的时候回出现大量的get,post数据。

    VS2013新增的Browser Link功能虽然“强大”,但默认是开启的,很烦人!

  6. Chomp!游戏 (组合游戏Combinatorial Games)

    http://blog.csdn.net/acdreamers/article/details/17021095 有一个n*m的棋盘,每次可以取走一个方格并拿掉它右边和上面的所有方格.拿到左下角的格子 ...

  7. Escape Sequences in String

     Code Output \' single quote \" double quote \\ backslash \n newline \r carriage return \t tab ...

  8. 移动端web学习总结

    前言: 一直想做一个移动端的阶段性学习总结,但是工作太忙总是加班.现在总算可以抽出一点时间来写一写,把知道的都写下来,这样就算忘掉了,也能很快想起来,不要太机智啊,哈哈哈! 一.移动端页面常识 1.常 ...

  9. ant design 自定义表单验证大全

     需求是 账号名可以是手机号也可以是邮箱 要做手机号和邮箱的验证,官网的那个验证规则不匹配  怎么自定义验证规则?  一:组件部分 <Form horizontal> <Row gu ...

  10. 【转】The content of element type "configuration" must match "(properties?,settings?,typeAliases?,typeHandlers?,objectFactory?...

    [转]The content of element type "configuration" must match "(properties?,settings?,typ ...