poj-2104 K-th Number[主席树/函数式线段树/可持久化线段树]
来源:互联网 发布:单片机可靠性 编辑:程序博客网 时间:2024/06/05 01:52
膜拜大神:点击打开链接点击打开链接
【题目描述】有n个数字排成一列,有m个询问,格式为:left right k .即问在区间[left,right]第k大的数据为多少?
纯属个人理解,有不正确的地方欢迎留言指正:
先来设想下如何解决这个问题。
把数字在数组中的位置i作为定义域,数字的值v[i]作为值域。
假如对于 [left,right]的数我们能知道它们的值域在不同区间出现的个数,就可以根据出现个数来二分查找来找的第k大的值,而区间出现的个数可以用线段树来存储。
为了避免浪费空间,将值域离散化存进线段树。对于某段值域[l,r],[left,right]中数字出现的个数 =[0,right]中数字出现的个数-[0,left-1]中数字出现的个数
那么就可以建立 n+1颗线段树,第i颗线段树记录[0,i]数字的值域的区间出现次数。
举个栗子:
410 31 42 15这个数据按照上面思路可以建立5个线段树
很明显,时间效率和空间效率都很高。
从图上也能很直观的看出每个线段树之间有很多可以共用的节点。
可持久化数据结构
在算法执行的过程中,会发现在更新一个动态集合时,需要维护其过去的版本。这样的集合称为是可持久的。
实现持久集合的一种方法时每当该集合被修改时,就将其整个的复制下来,但是这种方法会降低执行速度并占用过多的空间。
可持久化数据结构(Persistent data structure)就是利用函数式编程的思想使其支持询问历史版本、同时充分利用它们之间的共同数据来减少时间和空间消耗。
单点更新
第i个树建立的时候,是在第i-1个树的基础上把某条从树根到叶子节点的路径上的节点+1。这条路径上的节点是必须新建的,非此路径的节点可以与上颗树共用的,与它共用。
这明显只能用链式结构,所以不能对线段树用标号实现了。
结构
struct node{node *ls,*rs;int cnt;};
第0颗树就只能单独建立了
node *build(int l,int r){//构建第0个线段树node *rt=get_nd();if(l==r){rt->cnt=0,rt->ls=rt->rs=NULL;return rt;}int mid=(l+r)>>1;rt->ls=build(l,mid);rt->rs=build(mid+1,r);/push_up(rt);return rt;}
构建第1颗到第n颗线段树
//pos为第i个数的离散化后的值域,val=1表示增加的个数//pre是第i-1颗线段树的指针node *update(node *pre,int l,int r,int pos,int val){//在前一个线段树的基础上构建此次线段树node *rt=get_nd();*rt=*pre;if(l==pos&&r==pos){rt->cnt+=val;return rt;}int mid=(l+r)>>1;if(pos<=mid) rt->ls=update(pre->ls,l,mid,pos,val);else rt->rs=update(pre->rs,mid+1,r,pos,val);push_up(rt);return rt;}
查询第k小个值
int query(node *cur,node *pre,int l,int r,int kth){//查询if(l==r) return l;int mid=(l+r)>>1;int lim=cur->ls->cnt-pre->ls->cnt;if(kth<=lim) return query(cur->ls,pre->ls,l,mid,kth);else return query(cur->rs,pre->rs,mid+1,r,kth-lim); }
poj2104 代码:
/*【题目描述】有n个数字排成一列,有m个询问,格式为:left right k 即问在区间[left,right]第k大的数据为多少?*/#include <stdio.h>#include <string.h>#include <stdlib.h>#include <algorithm>#define find_max(a,b) a>b?a:busing namespace std;const int maxn=100008;struct node{node *ls,*rs;int cnt;}rsta[maxn*30];//内存池int scnt,n,m;node *rot[maxn];//第i个线段树的根节点int v[maxn];//每个数字的原值int dv[maxn];//每个数字的离散值int sub_v[maxn];//v排序后的备份int maxv;//最大的离散值inline node *get_nd(){//从预开的内存池中得到一个节点return &rsta[scnt++];}inline void push_up(node *rt){//上压rt->cnt=rt->ls->cnt+rt->rs->cnt;}node *build(int l,int r){//构建第0个线段树node *rt=get_nd();if(l==r){rt->cnt=0,rt->ls=rt->rs=NULL;return rt;}int mid=(l+r)>>1;rt->ls=build(l,mid);rt->rs=build(mid+1,r);/push_up(rt);return rt;}node *update(node *pre,int l,int r,int pos,int val){//在前一个线段树的基础上构建此次线段树node *rt=get_nd();*rt=*pre;if(l==pos&&r==pos){rt->cnt+=val;return rt;}int mid=(l+r)>>1;if(pos<=mid) rt->ls=update(pre->ls,l,mid,pos,val);else rt->rs=update(pre->rs,mid+1,r,pos,val);push_up(rt);return rt;}int query(node *cur,node *pre,int l,int r,int kth){//查询if(l==r) return l;int mid=(l+r)>>1;int lim=cur->ls->cnt-pre->ls->cnt;if(kth<=lim) return query(cur->ls,pre->ls,l,mid,kth);else return query(cur->rs,pre->rs,mid+1,r,kth-lim); }void discretize(){//离散化for(int i=1;i<=n;++i)sub_v[i]=v[i];sort(sub_v+1,sub_v+n+1);int size=unique(sub_v+1,sub_v+n+1)-sub_v-1;//size为离散化后元素个数for(int i=1;i<=n;++i)//k为b[i]经离散化后对应的值{dv[i]=lower_bound(sub_v+1,sub_v+size+1,v[i])-sub_v;maxv=find_max(dv[i],maxv);}}void solve(){scnt=0,maxv=0;discretize();int t=maxv,r=1;while(t) t/=2,r*=2;//求值域范围rot[0]=build(1,r);for(int i=1;i<=n;++i)rot[i]=update(rot[i-1],1,r,dv[i],1);int x,y,k;while(m--){scanf("%d%d%d",&x,&y,&k);k=query(rot[y],rot[x-1],1,r,k);//得到的是离散值printf("%d\n",sub_v[k]);}}int main(){while(~scanf("%d%d",&n,&m)){for(int i=1;i<=n;++i)scanf("%d",v+i);solve();}return 0;}
- poj-2104 K-th Number[主席树/函数式线段树/可持久化线段树]
- 主席树(可持久化线段树)讲解 [POJ 2104] K-th Number
- POJ 2104 K-th Number (可持久化线段树)
- [poj]-2104-K-th Number-可持久化线段树
- poj 2104 K-th Number 可持久化线段树
- POJ 2104 K-th Number(可持久化线段树)
- POJ 2104 K-th Number 可持久化线段树
- POJ2104-K-th Number-区间第k大-可持久化线段树/主席树
- POJ 2104 K-th Number 区间第K大,可持久化线段树
- POJ 2104K-th Number(可持久化线段树-求第K大)
- 【POJ2104】K-th Number-主席树(可持久化线段树)+离散化
- 【POJ2104】K-th Number 主席树?函数式线段树?可持久化线段树?……反正是其中一个
- POJ 2104 K-th number 主席树 函数式线段树
- poj2104 K-th Number 离散+可持久化线段树
- poj 2104 K-th Number 函数式线段树
- poj 2104 K-th Number(函数式线段树)
- POJ-2104-K-th Number(函数式线段树)
- 可持久化线段树|主席树 POJ 2104 区间第k大的数
- Android项目结构与源码分析,解开其中秘密
- [BFS]马的遍历
- Android studio 环境配置(未完待续)
- LeetCode 004 Median of Two Sorted Arrays *
- HDU2099 整除的尾数
- poj-2104 K-th Number[主席树/函数式线段树/可持久化线段树]
- 不知道是什么(ting)(shuo)效率很高的代码
- Quartz实现动态定时任务
- LeetCode 332. Reconstruct Itinerary(重构行程)
- leetcode-Sqrt(x)与Bad version
- AOP概念
- display:block
- 我的模板库
- RecyclerView的 setEmptyView