题意:给定一个序列A,接下来又m个询问,每个询问输出A[L,R]中的第K大。(保证第k大存在)

思路:

  我想拿来练习“可持久化线段树”的,搜到这个比较巧的算法也可以解决这个问题,叫“归并树?。大概的思想就是和线段树一样,只是线段树上的每个非叶子节点是一个区间,等于该节点的两个孩子节点的区间的拼接起来,而每个区间内保持有序的。那么在查找时就找到这两个区间,二分枚举答案然后在询问区间[L,R]判断否排第k。这里二分答案只需要在线段树的根进行就行了,因为根这个区间是有序的。查找时[L,R]可能会是两个区间的拼接的[L,mid]+[mid+1,R],所以要在两个区间中分别判断val排行老几,然后加起来就是其在[L,R]的真实排行了,这可以用low_bound函数实现。

 //#include <bits/stdc++.h>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>
#include <vector>
#include <algorithm>
#include <iostream>
#define pii pair<int,int>
#define INF 0x3f3f3f3f
#define LL long long
using namespace std;
const int N=;
int seq[N], tree[][N]; void build_tree(int L,int R,int depth)
{
if(L==R)
{
tree[depth][L]=seq[L];
return;
}
int mid=(L+R)>>;
build_tree(L,mid,depth+);
build_tree(mid+,R,depth+); //归并
int first=L, second=mid+, cnt=L;
while( first<=mid && second<=R )
{
if(tree[depth+][first]<tree[depth+][second])
tree[depth][cnt++]=tree[depth+][first++];
else
tree[depth][cnt++]=tree[depth+][second++];
}
if(first<=mid) //左边未完
{
for(int i=first; i<=mid; i++)
tree[depth][cnt++]=tree[depth+][i];
}
else //右边未完
{
for(int i=second; i<=R; i++)
tree[depth][cnt++]=tree[depth+][i];
}
} int query(int ll,int rr,int L,int R,int val,int depth) //返回val在[L,R]内的排名-1
{
if(L==ll && rr==R)
return lower_bound( &tree[depth][L], &tree[depth][R+], val)
-&tree[depth][L];
int mid=(ll+rr)>>, cnt=;
if( R<=mid ) cnt+=query(ll,mid, L,R, val,depth+);
else if( L>mid ) cnt+=query(mid+,rr, L,R, val,depth+);
else
{
cnt+=query(ll,mid, L,mid, val,depth+);
cnt+=query(mid+,rr, mid+,R, val,depth+);
}
return cnt;
} int main()
{
freopen("input.txt", "r", stdin);
int n, m, L, R, k;
while(~scanf("%d%d",&n,&m))
{
for(int i=; i<=n; i++) scanf("%d",&seq[i]);
build_tree(, n, ); while(m--)
{
scanf("%d%d%d",&L,&R,&k);
k--;
int ll=, rr=n;
while( ll<rr ) //在tree[0]中二分这个数
{
int mid=ll+(rr-ll+)/;
int pos=query(,n, L,R, tree[][mid],);
if( pos<=k ) ll=mid; //所查找的数太小了
else rr=mid-;
}
printf("%d\n", tree[][ll]);
}
}
return ;
}

AC代码

  主席树解法:按照序列的顺序seq[i],每插入1个点就建1棵树,而每棵树中有且只有seq[1,i]这个序列,而且不是按照seq[1,i]的顺序,而是变成在该树中是有序的。

  举例:假设有序列seq[4]={1,3,2,4}。

  插入第seq[1]后的结果:

  

  插入第seq[2]后的结果:

  

  插入第seq[3]后的结果:

  

  插入第seq[4]后的结果:

  

  观察上面的4张图,红色的点表示是不同于上一幅图的的点,即是新创建的的点。可以看到,每次插入后最多仅有logn个点会被创建。插入是按照有序的方式插入的,比如新插入seq[2]=2,那么其应该排在第二,所以我们需要事先对seq进行排序,才能知道seq[i]的具体应该插在什么位置。

  得到这些图就可以O(logN)知道第k个数了,比如要在区间[3,4]中找k=1的数字,那么只需要根据root[2]和root[4]就可以算出,只需要在每个节点上用个计数器cnt表示该子树的节点数,具体的话不难算的,自己研究下图吧。

 //#include <bits/stdc++.h>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>
#include <algorithm>
#include <vector>
#include <iostream>
#define pii pair<int,int>
#define INF 0x3f3f3f3f
#define LL long long
using namespace std;
const int N=;
struct Node
{
int L, R, cnt;
}nod[N*]; //线段树上的节点
struct Seq
{
int val,idx;
bool operator < ( const Seq &t ) const{return val<t.val;}
}seq[N]; //序列
int rank[N], root[N], node_cnt; void insert(int rk,int &t,int L,int R) //每次插入,就建1棵新树
{
nod[node_cnt]=nod[t];
t=node_cnt++;
nod[t].cnt++; //此子树的叶子节点数 if(L==R) return ; //到底了。只存此子树的节点数 int mid=(L+R)>>;
if(rk<=mid) insert(rk,nod[t].L, L,mid);
else insert(rk,nod[t].R, mid+,R);
} int query(int t1,int t2,int k,int L,int R)
{
if(L==R) return R; //返回的是“有序序列”的下标
int L1=nod[t1].L, L2=nod[t2].L; //两棵树的左子树节点数量
int left=nod[L2].cnt-nod[L1].cnt; //用于判断第k大在左/右
int mid=(L+R)>>; if(k<=left) query(nod[t1].L, nod[t2].L, k, L, mid); //在左边
else query(nod[t1].R, nod[t2].R, k-left, mid+, R );
} int main()
{
//freopen("input.txt", "r", stdin);
int n, m, L, R, k;
while(~scanf("%d%d",&n,&m))
{
node_cnt=;
memset(root, , sizeof(root));
for(int i=; i<=n; i++)
{
scanf("%d",&seq[i].val);
seq[i].idx=i;
}
sort(seq+,seq+n+); //需先排序
for(int i=; i<=n; i++) //反向索引
rank[ seq[i].idx ]=i;
for(int i=; i<=n; i++) //按原序逐个插入
{
root[i]=root[i-];
insert(rank[i], root[i], , n);
}
while(m--)
{
scanf("%d%d%d",&L,&R,&k);
int idx=query(root[L-], root[R], k, , n); //两树可以同时进行
printf("%d\n", seq[idx].val);
}
}
return ;
}

AC代码

POJ 2104 K-th Number (区间第k大)的更多相关文章

  1. poj 2104 主席树(区间第k大)

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

  2. [poj 2104]主席树+静态区间第k大

    题目链接:http://poj.org/problem?id=2104 主席树入门题目,主席树其实就是可持久化权值线段树,rt[i]维护了前i个数中第i大(小)的数出现次数的信息,通过查询两棵树的差即 ...

  3. POJ 2014.K-th Number 区间第k小 (归并树)

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

  4. HDU 2665.Kth number 区间第K小

    Kth number Time Limit: 15000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total ...

  5. POJ2104 K-th Number —— 区间第k小 整体二分

    题目链接:https://vjudge.net/problem/POJ-2104 K-th Number Time Limit: 20000MS   Memory Limit: 65536K Tota ...

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

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

  7. BZOJ 3110([Zjoi2013]K大数查询-区间第k大[段修改,在线]-树状数组套函数式线段树)

    3110: [Zjoi2013]K大数查询 Time Limit: 20 Sec   Memory Limit: 512 MB Submit: 418   Solved: 235 [ Submit][ ...

  8. POJ 2104 K-th Number(区间第k大数)(平方切割,归并树,划分树)

    题目链接: http://poj.org/problem? id=2104 解题思路: 由于查询的个数m非常大.朴素的求法无法在规定时间内求解. 因此应该选用合理的方式维护数据来做到高效地查询. 假设 ...

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

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

  10. POJ-2104-K-th Number(区间第K大+主席树模板题)

    Description You are working for Macrohard company in data structures department. After failing your ...

随机推荐

  1. <正则吃饺子> :关于Guava中 Joiner 和 Splitter 的简单使用

    在现在项目中经常看到 这两个类的使用,开始时候不明白具体是做的什么事情,就单独拿出来学习下了,参照了网上的博文,这里主要是简单的讲讲用法. 具体对这两个类,不做过多介绍,有个在线文档,需要的可以自己去 ...

  2. python 中 模块,包, 与常用模块

    一 模块 模块:就是一组功能的集合体, 我们的程序可以直接导入模块来复用模块里的功能 导入方式 一般为 : import 模块名 在python中, 模块一般分为四个通用类别 1使用python编写. ...

  3. HDU - 1495 非常可乐 bfs互倒三杯水

    非常可乐 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submis ...

  4. (3)ASP.NET Core 服务生命周期

    1.前言 在ConfigureServices方法中的容器注册每个应用程序的服务,Asp.Core都可以为每个应用程序提供三种服务生命周期:●Transient(暂时):每次请求都会创建一个新的实例. ...

  5. CentOS Linux 7 提示 lsof: 未找到命令

    我们常使用 lsof -i:端口号 命令来查看某端口是否开放,如使用下面的命令,查看8080端口: lsof -i: 结果: 提示:lsof:未找到命令 解决办法 使用yum来安装lsof,命令如下: ...

  6. JVM加载类的原理机制

    在Java中,类装载器把一个类装入Java虚拟机中,要经过三个步骤来完成:装载.链接和初始化,其中链接又可以分成校验.准备.解析装载:查找和导入类或接口的二进制数据: 链接:执行下面的校验.准备和解析 ...

  7. socket编程模拟linux下的ssh代码实现

    实现思路: 1.提供输入指令的客户端: 2.提供返回执行指令结果的服务端 3.寻找服务端返回结果一次无法全部接收的解决思路 服务端代码(ssh_server.py) #coding=utf-8 imp ...

  8. Qt 2D绘图之五:图形视图框架的结构和坐标系统

    一.图形视图框架的结构 在前面讲的基本绘图中,我们可以自己绘制各种图形,并且控制它们.但是,如果需要同时绘制很多个相同或不同的图形,并且要控制它们的移动.检测它们的碰撞和叠加:或者我们想让自己绘制的图 ...

  9. HDU6300(2018多校第一场)

    Bryce1010模板 http://acm.hdu.edu.cn/showproblem.php?pid=6300 排个序就好了 #include<iostream> #include& ...

  10. ajax中get和post区别

    参考地址:http://blog.csdn.net/laijieyao/article/details/40426257 首先要明确的事$.get方法是使用GET方式进行异步请求.$.post方法使用 ...