很久没打代码了,不知道为什么,昨天考岭南文化之前突然开始思考起这个问题来,这个问题据说有很多种方法,划分树什么的,不过对于我现在这种水平还是用熟悉的线段树做比较好。这到题今年8月份的时候曾经做过,那个时候是作为对函数式线段树的一个基础题来做的,那个时候不懂,看着别人的代码对拍然后摸索下来,所以到了昨天我就已经彻底忘了,因为代码不是自己写的。昨天想了很久,终于参透了它的精髓。

首先对于给出的a[1]~a[n]这么多个数离散化,然后建立一个线段树,线段树中的结点对应的区间[L,R]表示的是在[L,R](离散化后的值)中有多少个数。

举个例子,a={1,3,2,6,4,7}  那么假如我对这整个数组建线段树,[3,6]的sum应该为2,因为[3,6]里只有3和5在数组里。

函数式的思想是,假如我建出来从a[1]~a[i]对应的线段树T[i],那么当我要去求数组中的某一段[x,y]在[L,R]里有多少个的时候,实际上就是T[y].[L,R]的和-T[x-1].[L,R]的和,这样我们就可以知道,在数组的a[x]~a[y]里在范围在L-R中的数有多少个。这样的话我们可以二分出第kth值。

即从0~size,如果0~mid里已经有超过k个了,说明第k大在左边,否则第k大在右边,这样查找就可以了。这次我终于写出了一个自己的二分。以前二分都是lower_bound水过,如果要用到的话,我会在l+1=r的时候特判,特别猥琐,看了一篇关于二分的总结才知道,二分的写法有分左闭右闭和左开右开的,不过这些都要慢慢领悟了。

再谈谈怎么建线段树,假如我们已经建了线段树T[i],那么当我们要建线段树T[i+1]的时候,如果再建一次空间会非常大,所以要充分利用T[i]的信息,实际上建立T[i+1]的时候只比T[i]多了一个值,所以我们只需要延着路径重建这些新的结点就好,另外一边的只需要用回历史版本的就好。延着线段树往下会修改到logn个结点,所以建立T[0]时需要开4*n,再加上后来每个一个都要logn个结点,所以总的结点数是(4+logn)*n.  logn=16.多,这也就是为什么我们的结点数要开到20倍,所以这种树对空间的消耗是蛮大的。

想想之前线段树都要套模板,现在可以自己敲出来,这一年里还是有进步的。我还是慢慢努力练习练习吧~

贴一记代码,1700ms,非常慢,权当学习- -0

#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<cstdio>
#include<cmath>
#define maxn 100000
using namespace std;struct Node
{int l,r,sum;Node *lc,*rc;
}N[20*maxn];
int top;Node *root[100050];
int n,m;
int a[maxn+20];
int b[maxn+20];
int bsize;Node *build(int L,int R)
{if(L==R){Node *ret=&N[top++];ret->l=L;ret->r=R;ret->sum=0;ret->lc=NULL;ret->rc=NULL;return ret;}else{Node* ret=&N[top++];int M=(L+R)>>1;ret->l=L;ret->r=R;ret->lc=build(L,M);ret->rc=build(M+1,R);ret->sum=ret->lc->sum+ret->rc->sum;return ret;}
}Node *build(int L,int R,int k,Node *p)  // 对key值k,和历史版本的p重建树
{if(L==R){Node *ret=&N[top++];ret->l=L;ret->r=R;ret->sum=p->sum+1;ret->lc=NULL;ret->rc=NULL;return ret;}else{int M=(L+R)>>1;Node *ret=&N[top++];ret->l=L;ret->r=R;if(k<=M){   ret->lc=build(L,M,k,p->lc); // k值在原树的左边的时候要重建左子树ret->rc=p->rc;  // 右子树不变,用历史版本ret->sum=ret->lc->sum+ret->rc->sum; // 更新和值return ret;}else{ret->rc=build(M+1,R,k,p->rc); // k值在原树的右边的时候要重建右子树ret->lc=p->lc; // 左子树不变,用历史版本ret->sum=ret->lc->sum+ret->rc->sum; // 更新和值return ret;}}
}int query(Node *p,int L,int R) // 对结点p的[L,R]区间求和值
{if(p->l==L&&p->r==R){return p->sum;}else{int M=(p->l+p->r)>>1;if(R<=M) return query(p->lc,L,R);else if(L>M) return query(p->rc,L,R);else return query(p->lc,L,M)+query(p->rc,M+1,R);}
}int search(int x,int y,int k) //找从x到y上的区间第k大
{int l=0,r=bsize;int mid,num;while(l<r){mid=(l+r)>>1;num=query(root[y],l,mid)-query(root[x-1],l,mid); // 算出<=mid的有多少个,利用区间可以减的性质if(k<=num) r=mid;else{k-=num;l=mid+1;}}return l;
}int main()
{while(cin>>n>>m){for(int i=0;i<n;++i){scanf("%d",&a[i]);b[i]=a[i];}//离散化的过程sort(b,b+n);bsize=unique(b,b+n)-b;for(int i=0;i<n;++i){a[i]=lower_bound(b,b+bsize,a[i])-b;}top=0;root[0]=build(0,bsize);for(int i=1;i<=n;++i){root[i]=build(0,bsize,a[i-1],root[i-1]); // 根据a[i-1]的值,以及上一个版本的树建立新的子树}int li,ri,ki;for(int i=0;i<m;++i){scanf("%d%d%d",&li,&ri,&ki);printf("%d\n",b[search(li,ri,ki)]);}}return 0;
}

转载于:https://www.cnblogs.com/chanme/p/3481917.html

POJ2104 K-th number 函数式线段树相关推荐

  1. 【bzoj3065】: 带插入区间K小值 详解——替罪羊套函数式线段树

    不得不说,做过最爽的树套树---- 由于有了区间操作,我们很容易把区间看成一棵平衡树,对他进行插入,那么外面一层就是平衡树了,这就与我们之前所见到的不同了.我们之前所见到的大多数是线段树套平衡树而此题 ...

  2. 【做练习】K-th Number(线段树)线段树的原理

    1. 题目 描述 You are working for Macrohard company in data structures department. After failing your pre ...

  3. HDU 1394 Minimum Inversion Number(线段树的单点更新)

    点我看题目 题意 :给你一个数列,a1,a2,a3,a4.......an,然后可以求出逆序数,再把a1放到an后,可以得到一个新的逆序数,再把a2放到a1后边,,,,,,,依次下去,输出最小的那个逆 ...

  4. HDU2665(函数式线段树-区间第K大)

    题目:K-th Number 如果求区间第K小,就转换一下就行了,假设你要求区间[u,v]的第k小,那就是第v-u+1-k大 #include<stdio.h> #include<a ...

  5. HDOJ1394 Minimum Inversion Number【线段树】

    需要注意的就是给出的数是0~n-1,而线段树根节点范围是1-n 所以main中insert要num[i]+1. 当然,根节点范围换成0~n-1就不需要了. Problem : 1394 ( Minim ...

  6. BZOJ-1901 Zju2112 Dynamic Rankings 函数式线段树 套 树状数组+离线处理

    1901: Zju2112 Dynamic Rankings Time Limit: 10 Sec Memory Limit: 128 MB Submit: 6058 Solved: 2521 [Su ...

  7. 【用学校抄作业带你走进可持久化线段树(主席树)】可持久化线段树概念+全套模板+例题入门:[福利]可持久化线段树)

    我似乎很少写这种算法博客 可持久化线段树概念 概念介绍(类比帮助理解) 简单分析一下时间和空间复杂度(内容池) 模板 结构体变量 建树模板 单点修改模板 单点查询模板 区间修改模板(pushup) 区 ...

  8. 解题报告:P3834 【模板】可持久化线段树 2(主席树)详解

    P3834 [模板]可持久化线段树 2(主席树) 题解 P3834 [[模板]可持久化线段树 2(主席树)] 1)静态求第k大数 可持久化线段树,不能用堆的方法存子结点了,所以用指针l表示左儿子r表示 ...

  9. 2018 UESTC 线段树专题

    A - 一棵简单的线段树 A[1...n]初始全为0. 1. 给两个数p 和 x(1≤p≤n),单点更新 A[p] <- x 2. 给两个数L和R (1≤L<R≤n),  L到R区间里这几 ...

最新文章

  1. 面试常考:Synchronized 有几种用法?
  2. UIAutomation 自动化
  3. 【限时免费报名】MongoDB原厂和阿里专家喊你来训练营上课啦!
  4. SAP Spartacus后台CMS Component和Angular Component的映射关系
  5. 情人节,我表白了CSDN小姐姐后,竟然...【为表白写了一个绘图工具,让我不再手残】
  6. java .vm h2_java-H2服务器在调试时挂起
  7. mysql执行语句后回退_MySQL命令学习笔记(八)
  8. jsp中JAVA代码取select值_jsp获取下拉列表select选择的值 | 学步园
  9. jetty-maven-plugin
  10. OpenLayers自定义圆锥投影中国地图
  11. linux开机出现repaire filesystem
  12. Yate学习--基于Windows安装和运行Yate
  13. IT 工程师长期保住饭碗的一些建议
  14. 2.Deep Crossing: Web-Scale Modeling without Manually Crafted Combinatorial Features论文核心解读以及代码实现
  15. AutoCAD C# 自动加载自定义RibbonUI界面
  16. 干货 :超详细的AI 专家路线图!
  17. 【UNR #6 C】稳健型选手(分治)(主席树)(二分)
  18. c语言 数字翻译成英文,100数字翻译成英语?
  19. 朴素版Dijkstra算法
  20. 智能控制(第4版)习题

热门文章

  1. python 生成器函数_Python 生成器函数
  2. 面向车载通信的安全关键技术【会议】
  3. Matlab中出现“无法打开电子表格,MATLAB报告了以下错误;错误:服务器出现意外情况”
  4. SSM整合(配置文件)
  5. 电脑ping_Windows系统——ping打开教程
  6. access订单明细表怎么做_图书销售订单明细表 (4)
  7. django+bootstrap_Django自学教程PDF高清文档下载
  8. jsp登录密码加密_[源码和文档分享]基于JSP和MYSQL数据库的在线购物网站的设计与实现...
  9. c语言linux下可用函数,[原创]linux下编码转换问题,C语言实现,使用iconv函数族68...
  10. crack翻译成中文_crack是什么意思_crack在线翻译_英语_读音_用法_例句_海词词典