poj2104 k-th number 主席树入门讲解

定义:主席树是一种可持久化的线段树 又叫函数式线段树

 

刚开始学是不是觉得很蒙逼啊 其实我也是

主席树说简单了 就是

保留你每一步操作完成之后的线段树 然后有可加减性

也就是说你每添加的一个点的那棵树都给你保留下来了

呃 。。。 这么说好像还是有点生涩

那么就拿poj2104来举例子吧 慢慢讲我觉得会很好的

题意就是给你一个100000长度的数字 然后100000次询问[L,R]之间第k大的数字是多少

这个很容易看出来 暴力根本不可以 黑你分分钟的事情啊

我们怎么办呢 想想线段树能不能做 想来想去 一颗线段树好像不能这么做 GG

那么我们做一个美好的假设:

我们建立100000棵美丽的线段树 每一个线段树的节点 表示这一个区间内有多少个数字

第一棵线段树保存着把第一个数字插入进去之后 每个区间有多少个数字

第二棵线段树保存着把第一个 第二个数字插入进去之后 每个区间有多少个数字

第n棵线段树保存着把第1,2,3。。。。n个数字插入进去之后 每个区间有多少个数字

好了 我们已经建立了这么多的线段树 我们接下来该怎么办呢?

对 就是查询

可是如何查询呢? 假设我们要查询[l,r]内的第k大

我们可以拿出第l-1 ,r 棵线段树,然后两者相减(两棵树的更节点相减) 我们想一下 这样不就得到了相当于插入了第l到r个点所建立的一棵线段树 这棵线段树每个节点保留的信息是:这个区间内数字的个数 然后我们往下二分查找 就可以得到第k大了

比如说r对应的那棵树1-15里面有10个,l-1对应的那棵树1-15有6,那么r-(l-1)的1-15有10-6=4个,如果我现在k=8,那么说明1-15满足不了我,比如我需要到6-20中去找,r那棵树。

所谓主席树呢,就是对原来的数列[1..n]的每一个前缀[1..i](1≤i≤n)建立一棵线段树,线段树的每一个节点存某个前缀[1..i]中属于区间[L..R]的数一共有多少个(比如根节点是[1..n],一共i个数,sum[root] = i;根节点的左儿子是[1..(L+R)/2],若不大于(L+R)/2的数有x个,那么sum[root.left] = x)。若要查找[i..j]中第k大数时,设某结点x,那么x.sum[j] - x.sum[i - 1]就是[i..j]中在结点x内的数字总数。而对每一个前缀都建一棵树,会MLE,观察到每个[1..i]和[1..i-1]只有一条路是不一样的,那么其他的结点只要用回前一棵树的结点即可,时空复杂度为O(nlogn)。

现在的问题时 这么庞大的空间开销我们耗费不起 我们该如何建立这样的线段树呢?

答案就是 我们要尽量利用重复节点

我们可以想一下 我们每次建立线段树 相对于前一棵线段树 我们只修改了它的一条路径 最多只有logn的变化 那么我们就存下这logn的变化 尽可能的利用重复节点 就可以达到重复使用的目的 有张图你们自己体会一下 我也是盗图 侵删~

每次只修改一条路径

这样就能完成我们的主席树了

接下来是我自己写的该题代码

  1. #include <iostream>
  2. #include <algorithm>
  3. #include <stdio.h>
  4. #include <string.h>
  5. #include <stdlib.h>
  6. using namespace std;
  7. #define maxn (int)(1e6+10)
  8. struct node{
  9. int cnt,l,r;
  10. }treenode[30*maxn];//定义一个结构体吧 要不心累 l 和 r 表示 左右两个节点的序号 这个不是单纯的单个线段树了 这个还是有必要的 最好开20-40倍
  11. int tree_cnt[maxn];//每个线段树跟节点的坐标 这个是搜索的起点啊
  12. int init[maxn];//
  13. int cop[maxn];
  14. int n,t_cnt=0,newn;//t_cnt是现在数组开到多大了 然后建立下一个的时候注意++t_cnt;
  15. int getid(int x) {return (int)(lower_bound(init,init+newn,x)-init);}//数据太大 需要离散化
  16. int insert(int num,int becopyed,int l,int r)//记住返回自己的坐标
  17. {
  18. ++t_cnt;
  19. treenode[t_cnt].cnt=treenode[becopyed].cnt+1;
  20. int save=t_cnt;
  21. int mid=(l+r)/2;
  22. if(l==r)
  23. {
  24. return save;
  25. }
  26. else if(num<=mid)
  27. {
  28. treenode[save].l=insert(num,treenode[becopyed].l,l,mid);
  29. treenode[save].r=treenode[becopyed].r;
  30. }
  31. else
  32. {
  33. treenode[save].r=insert(num,treenode[becopyed].r,mid+1,r);
  34. treenode[save].l=treenode[becopyed].l;
  35. }
  36. return save;
  37. }
  38. int query(int x,int y,int k,int l,int r)
  39. {
  40. if(l==r) return l;
  41. int p=(treenode[treenode[y].l].cnt-treenode[treenode[x].l].cnt);
  42. int mid=(l+r)/2;
  43. if(k<=p)
  44. {
  45. return query(treenode[x].l,treenode[y].l,k,l,mid);
  46. }
  47. else return query(treenode[x].r,treenode[y].r,k-p,mid+1,r);
  48. }//一边做减法 一边查询
  49. void print(int x,int l,int r)
  50. {
  51. cout<<treenode[x].cnt<<' ';
  52. if(l==r) {return;}
  53. int mid=(l+r)>>1;
  54. print(treenode[x].l,l,mid);
  55. print(treenode[x].r,mid+1,r);
  56. }
  57. int main()
  58. {
  59. int n,qnum;
  60. cin>>n>>qnum;
  61. for(int i=0;i<n;++i) {scanf("%d",init+i);cop[i]=init[i];}
  62. sort(init,init+n);
  63. newn=unique(init,init+n)-init;
  64. for(int i=1;i<=n;++i)
  65. {
  66. int p=insert(getid(cop[i-1]),tree_cnt[i-1],0,n);
  67. tree_cnt[i]=p;
  68. //cout<<p<<endl;
  69. }
  70. for(int i=0;i<qnum;++i)
  71. {
  72. int x,y,k;
  73. scanf("%d %d %d",&x,&y,&k);
  74. //cin>>x>>y>>k;
  75. int ans=query(tree_cnt[x-1],tree_cnt[y],k,0,n);
  76. //cout<<ans<<endl;
  77. printf("%d\n",init[ans]);
  78. //cout<<init[ans]<<endl;
  79. }
  80. //cout<<endl;
  81. //for(int i=0;i<=n;++i) print(tree_cnt[i],0,n),cout<<endl;
  82. return 0;
  83. }

poj2104 k-th number 主席树入门讲解的更多相关文章

  1. 【poj2104】K-th Number 主席树

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

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

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

  3. 主席树入门(区间第k大)

    主席树入门 时隔5个月,我又来填主席树的坑了,现在才发现学算法真的要懂了之后,再自己调试,慢慢写出来,如果不懂,就只会按照代码敲,是不会有任何提升的,都不如不照着敲. 所以搞算法一定要弄清原理,和代码 ...

  4. poj 2104 K-th Number (划分树入门 或者 主席树入门)

    题意:给n个数,m次询问,每次询问L到R中第k小的数是哪个 算法1:划分树 #include<cstdio> #include<cstring> #include<alg ...

  5. 静态区间第k大(主席树)

    POJ 2104为例(主席树入门题) 思想: 可持久化线段树,也叫作函数式线段树,也叫主席树(高大上). 可持久化数据结构(Persistent data structure):利用函数式编程的思想使 ...

  6. poj 2104 K-th Number 主席树+超级详细解释

    poj 2104 K-th Number 主席树+超级详细解释 传送门:K-th Number 题目大意:给出一段数列,让你求[L,R]区间内第几大的数字! 在这里先介绍一下主席树! 如果想了解什么是 ...

  7. poj2104 K-th Number区间第k小值 主席树

    原来主席树就是可持久化线段树啊,刚知道,,, 作为一道裸题,还是必A的,然而一开始偷懒不写离散化跪了N多遍,后来在缪大的帮助下发现了这个问题,遂A之 ——又是这种破问题,实在不想说自己了 把n个数看成 ...

  8. 【POJ2104】【HDU2665】K-th Number 主席树

    [POJ2104][HDU2665]K-th Number Description You are working for Macrohard company in data structures d ...

  9. POJ2104 K-th Number[主席树]【学习笔记】

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

随机推荐

  1. xmpp 消息和好友上下线(3)

    原始地址:XMPPFrameWork IOS 开发(四) 消息 //收到消息 - (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XM ...

  2. python3 http.server备忘

    python3英文的 打印出来应该不错: https://docs.python.org/3/library/http.server.html#module-http.server python2.7 ...

  3. 洛谷 4251 [SCOI2015]小凸玩矩阵

    [题解] 二分答案+二分图匹配. 先二分最小值Min,然后扫一遍这个矩阵,把满足a[i][j]<=Min的i,j连边,之后跑二分图匹配,如果最大匹配数大于等于n-k+1,当前的Min即是合法的. ...

  4. Codeforces 934D/933B - A Determined Cleanup

    传送门:http://codeforces.com/contest/934/problem/D 给定两个正整数p(p≥1).k(k>1).多项式f(x)的系数的取值集合为{0,1,2,...,k ...

  5. node-sass 安装失败

    安装 npm install 时偶尔遇到报错:没有安装python或node-sass 安装失败的问题,百度之后发现是被墙了,但根据百度的方法换了淘宝镜像和用了vpn都安装失败, 原因可能是没有卸载之 ...

  6. CodeForcesGym 100753F Divisions

    Divisions Time Limit: 2000ms Memory Limit: 262144KB This problem will be judged on CodeForcesGym. Or ...

  7. POJ 2217 Secretary

    Secretary Time Limit: 1000ms Memory Limit: 65536KB This problem will be judged on PKU. Original ID:  ...

  8. 单例模式(C#实现)

    这是这段时间学习设计模式的时候的源代码. 单例(单件)模式的五种实现. 通过一个计数器的例子调用验证一下. 把下面的代码直接拷进vs下,运行就可以了.(控制台应用程序) 以后把剩余的设计模式有空儿就粘 ...

  9. [bzoj1574][Usaco2009 Jan]地震损坏Damage_dfs

    地震损坏Damage bzoj-1574 Usaco-2009 Jan 题目大意:题目链接. 注释:略. 想法: 显然对于每一个report点,和它直接相连的点都不可能到达1.我们将它打上标记. 然后 ...

  10. Ubuntu 16.04解决在虚拟终端(Ctrl+Alt+F1)下显示菱形中文乱码问题

    在安装Ubuntu时,如果默认选择了中文,那么以后进去到虚拟终端就会出现菱形的中文乱码. 其实这个是无解的,但是可以通过以下技巧去实现: 1.把系统转成英文的 sudo gedit /etc/defa ...