POJ 2104 K-th Number (区间第k大)
题意:给定一个序列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大)的更多相关文章
- poj 2104 主席树(区间第k大)
K-th Number Time Limit: 20000MS Memory Limit: 65536K Total Submissions: 44940 Accepted: 14946 Ca ...
- [poj 2104]主席树+静态区间第k大
题目链接:http://poj.org/problem?id=2104 主席树入门题目,主席树其实就是可持久化权值线段树,rt[i]维护了前i个数中第i大(小)的数出现次数的信息,通过查询两棵树的差即 ...
- POJ 2014.K-th Number 区间第k小 (归并树)
K-th Number Time Limit: 20000MS Memory Limit: 65536K Total Submissions: 57543 Accepted: 19893 Ca ...
- HDU 2665.Kth number 区间第K小
Kth number Time Limit: 15000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total ...
- POJ2104 K-th Number —— 区间第k小 整体二分
题目链接:https://vjudge.net/problem/POJ-2104 K-th Number Time Limit: 20000MS Memory Limit: 65536K Tota ...
- POJ 2104:K-th Number(整体二分)
http://poj.org/problem?id=2104 题意:给出n个数和m个询问求区间第K小. 思路:以前用主席树做过,这次学整体二分来做.整体二分在yr大佬的指点下,终于大概懂了点了.对于二 ...
- BZOJ 3110([Zjoi2013]K大数查询-区间第k大[段修改,在线]-树状数组套函数式线段树)
3110: [Zjoi2013]K大数查询 Time Limit: 20 Sec Memory Limit: 512 MB Submit: 418 Solved: 235 [ Submit][ ...
- POJ 2104 K-th Number(区间第k大数)(平方切割,归并树,划分树)
题目链接: http://poj.org/problem? id=2104 解题思路: 由于查询的个数m非常大.朴素的求法无法在规定时间内求解. 因此应该选用合理的方式维护数据来做到高效地查询. 假设 ...
- POJ 2104:K-th Number(主席树静态区间k大)
题目大意:对于一个序列,每次询问区间[l,r]的第k大树. 分析: 主席树模板题 program kthtree; type point=record l,r,s:longint; end; var ...
- POJ-2104-K-th Number(区间第K大+主席树模板题)
Description You are working for Macrohard company in data structures department. After failing your ...
随机推荐
- Acyclic Organic Compounds
题意: 给一以1为根的字符树,给出每个节点的字符与权值,记 $diff_{x}$ 为从 $x$ 出发向下走,能走到多少不同的字符串,求问最大的$diff_{x} + c_{x}$,并求有多少个 $di ...
- Hadoop 源代码组织结构
Hadoop 2.X 包括 编译好的可以直接部署的文件hadoop-{VERSION}.tar.gz; 还有源代码文件hadoop-{VERSION}-src.tar.gz , 需要 Maven 编译 ...
- CF-831C
C. Jury Marks time limit per test 2 seconds memory limit per test 256 megabytes input standard input ...
- Asset Catalog Help (八)---Customizing Image Sets for Devices
Customizing Image Sets for Devices Add images to a set that are customized for display on the device ...
- js页面的全屏展示和退出全屏显示
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <HTML> <HEAD ...
- yzm10铺瓷砖 一只小蜜蜂 ycb与取款机
yzm10铺瓷砖 一天yzm10接到任务,要求用2×1大小的瓷砖,来铺2×4的地面,地面需要恰好被铺满.这对yzm10来说太容易了,于是他马上设计出了5种不同的铺法(旋转情况算不同种,如图示2.4). ...
- 快速打开和关闭SQL服务
将下面文本复制到txt中,然后将文本文件另存为bat @echo off for /f "skip=3 tokens=4" %%i in ('sc query MSSQLSERVE ...
- Java内存模型(Java Memory Model,JMM)
今天简单聊聊什么叫做 Java 内存模型,不是 JVM 内存结构哦. JMM 是一个语言级别的内存模型,处理器的硬件模型是硬件级别,Java中的内存模型是内存可见性的基本保证.从而为我们 volati ...
- pycharm 中切换虚拟环境
在pycharm上创建虚拟环境,网上的资料非常多. 如果pycharm上有多个项目,如何切换每个项目的虚拟环境? cmd 命令进入虚拟环境所在的文件夹(Pycharm在每创建一个新项目时就会创建一个虚 ...
- java快速排序代码
public class test1 { public static int partition(int[] array,int lo,int hi){ int key=array[lo]; whil ...