题目链接:https://www.luogu.com.cn/problem/P3834

对于区间查询第k小的问题,在区间数量达到5e5的时候是难以用朴素数据结构实现的,这时候主席树就应运而生了,主席树的最基础模板就是查询区间第k小树,其实他在可持久化操作上是十分上手的。主席树在线段树和离散化的基础上实现,树中每一个结点存的是当前结点代表的区间中数的数量,所以初始时刻每个结点的值都是零。然后要插入一个数a到达位置a,并且向上更新所有包含位置a的区间。主席树中每次要插入一个数就新建O(logn)量级的结点,其余结点与前一个树共用,这就实现了空间复杂度的最小化和空间的充分利用。我们在查询[L,R]区间的第K大的数的时候就要利用主席树的函数性质,即其各结点状态可减性,我们只要使得根节点编号为R的与根节点编号为L-1的两个树在结点同步时相减就能得到一棵[L,R]区间状态的树,进而用分治的思想查询树中的第K小。

以下是我手写的一份模板,用的是struct存状态,主席树的代码还是非常简单的,笔者为了让读者更好地理解,写了详细的注释:

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. typedef unsigned int ui;
  4. typedef long long ll;
  5. typedef unsigned long long ull;
  6. #define pf printf
  7. #define mem(a,b) memset(a,b,sizeof(a))
  8. #define prime1 1e9+7
  9. #define prime2 1e9+9
  10. #define pi 3.14159265
  11. #define lson l,mid,rt<<1
  12. #define rson mid+1,r,rt<<1|1
  13. #define scand(x) scanf("%llf",&x)
  14. #define f(i,a,b) for(int i=a;i<=b;i++)
  15. #define scan(a) scanf("%d",&a)
  16. #define mp(a,b) make_pair((a),(b))
  17. #define P pair<int,int>
  18. #define dbg(args) cout<<#args<<":"<<args<<endl;
  19. #define inf 0x3f3f3f3f
  20. const int maxn=2e5+;
  21. int n,m;
  22. int a[maxn];
  23. vector<int>v;//离散化之后将内容存储在vector中
  24. int getid(int x)
  25. {
  26. return lower_bound(v.begin(),v.end(),x)-v.begin()+;//下标是从1开始直到n结束
  27. }
  28. struct node{
  29. int l,r,sum;
  30. //分别保存左右子树的根节点的编号以及当前结点的数值,
  31. //在还没插入点信息的时候子树中每个结点的值都是0,所以主席树不需要建树,是边插入边建树的
  32. }t[maxn*]; //适应O(nlogn)空间需求,2^40次方大小的数据是不可能的,所以可以根据习惯进行选择
  33. int cnt=,root[maxn];
  34. //权值线段树中插入的值p就是插到位置p ,由于当前结点是需要变化的,所以传入引用使他指向内存池中新的结点
  35. void insert(int l, int r,int pre,int& now,int p)//参数中有前面一棵树的结点以及当前树的结点,
  36. {
  37. t[++cnt]=t[pre];//从内存池中生成一个根结点并将前面树的根节点复制到其中,
  38. now=cnt; //使当前结点指向新生成的根结点,
  39. t[now].sum++;
  40. //要在第p位上插入,所以当前访问的结点是一定是增1的,
  41. //后面我们将会决定访问左子树的结点还是右子树的结点
  42. if(l==r)return;//到达了点信息而且前面已经在叶子结点上面更新过点信息,所以直接return
  43. int m=l+r>>;
  44. //如果左子树的区间包括了p点就向左子树递归,否则走右子树,同时也要分别移动到左子树和右子树
  45. if(p<=m)insert(l,m,t[pre].l,t[now].l,p);
  46. else insert(m+,r,t[pre].r,t[now].r,p);
  47. }
  48. int query(int l,int r,int L,int R,int k)//两个结点同步相减
  49. {
  50. if(l==r)return l;//返回的是点信息,就是离散化之后的坐标,便于之后通过vector进行索引
  51. int m=l+r>>;
  52. int tmp=t[t[R].l].sum-t[t[L].l].sum;//先获取两棵树左子树的键值之差决定向哪一棵数递归
  53. if(k<=tmp)return query(l,m,t[L].l,t[R].l,k);
  54. else return query(m+,r,t[L].r,t[R].r,k-tmp);//如果tmp<k,就查询右子树中的第k-tmp小的数,有点分治的意味
  55. }
  56. int main()
  57. {
  58. //freopen("input.txt","r",stdin);
  59. //freopen("output.txt","w",stdout);
  60. std::ios::sync_with_stdio(false);
  61. scan(n);
  62. scan(m);
  63. f(i,,n)
  64. {
  65. scan(a[i]);
  66. v.push_back(a[i]);
  67. }
  68. sort(v.begin(),v.end());
  69. //将数列先排序再去重放入vector中以便通过位置获取在线段树中代表的区间点信息
  70. v.erase(unique(v.begin(),v.end()),v.end());
  71. //unique函数将不重复元素放到vector的前部,
  72. //返回的时所有不重复元素的下一个位置,所以将后面的元素删去就可以离散的成为线段树的坐标点信息
  73. f(i,,n)
  74. {
  75. insert(,n,root[i-],root[i],getid(a[i]));//在第i-1棵树上建第i棵树,所以传入的是两棵树的根节点
  76. }
  77. int l,r,k;
  78. while(m--)
  79. {
  80. scanf("%d%d%d",&l,&r,&k);
  81. pf("%d\n",v[query(,n,root[l-],root[r],k)-]);
  82. //查询[l,r]区间更新的信息,实现动态查询第k小,
  83. //注意vector中下标是从0开始的,而线段树中下标是从1开始的,所以错位了1,为了达到原来的元素就要减一
  84. }
  85. }

hdu2665 Kth number:

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. typedef unsigned int ui;
  4. typedef long long ll;
  5. typedef unsigned long long ull;
  6. #define pf printf
  7. #define mem(a,b) memset(a,b,sizeof(a))
  8. #define prime1 1e9+7
  9. #define prime2 1e9+9
  10. #define pi 3.14159265
  11. #define lson l,mid,rt<<1
  12. #define rson mid+1,r,rt<<1|1
  13. #define scand(x) scanf("%llf",&x)
  14. #define f(i,a,b) for(int i=a;i<=b;i++)
  15. #define scan(a) scanf("%d",&a)
  16. #define mp(a,b) make_pair((a),(b))
  17. #define P pair<int,int>
  18. #define dbg(args) cout<<#args<<":"<<args<<endl;
  19. #define inf 0x3f3f3f3f
  20. const int maxn=2e5+;
  21. int n,m,q;
  22. vector<int> v;
  23. int cnt,root[maxn];
  24. int getid(int x)
  25. {
  26. return lower_bound(v.begin(),v.end(),x)-v.begin()+;
  27. }
  28. int a[maxn];
  29. struct node{
  30. int l,r,sum;
  31. }t[maxn*];
  32. void insert(int l,int r,int pre,int &now,int pos)
  33. {
  34. t[++cnt]=t[pre];
  35. now=cnt;
  36. t[now].sum++;
  37. if(l==r)return;
  38. int m=l+r>>;
  39. if(pos<=m)insert(l,m,t[pre].l,t[now].l,pos);
  40. else insert(m+,r,t[pre].r,t[now].r,pos);
  41. }
  42. int query(int l,int r,int L,int R,int k)
  43. {
  44. if(l==r)return l;
  45. int tmp=t[t[R].l].sum-t[t[L].l].sum;
  46. int m=l+r>>;
  47. if(k<=tmp)return query(l,m,t[L].l,t[R].l,k);
  48. else return query(m+,r,t[L].r,t[R].r,k-tmp);
  49. }
  50. int main()
  51. {
  52. //freopen("input.txt","r",stdin);
  53. //freopen("output.txt","w",stdout);
  54. std::ios::sync_with_stdio(false);
  55. scan(q);
  56. f(kk,,q)
  57. {
  58. scan(n);scan(m);
  59. v.clear();
  60. cnt=;
  61. mem(root,);
  62. f(i,,n)
  63. {
  64. scan(a[i]);
  65. v.push_back(a[i]);
  66. }
  67. sort(v.begin(),v.end());
  68. v.erase(unique(v.begin(),v.end()),v.end());
  69. f(i,,n)insert(,n,root[i-],root[i],getid(a[i]));
  70. int l,r,k;
  71. f(tt,,m)
  72. {
  73. scanf("%d%d%d",&l,&r,&k);
  74. pf("%d",v[query(,n,root[l-],root[r],k)-]);
  75. pf("\n");
  76. }
  77. }
  78. }

洛谷3834 hdu2665主席树模板,动态查询区间第k小的更多相关文章

  1. poj 2761 主席树的应用(查询区间第k小值)

    Feed the dogs Time Limit: 6000MS   Memory Limit: 65536K Total Submissions: 22084   Accepted: 7033 De ...

  2. 主席树总结(经典区间第k小问题)(主席树,线段树)

    接着上一篇总结--可持久化线段树来整理吧.点击进入 这两种数据结构确实有异曲同工之妙.结构是很相似的,但维护的主要内容并不相同,主席树的离散化.前缀和等思想也要更难理解一些. 闲话 话说刚学习主席树的 ...

  3. 【可持久化线段树】POJ2104 查询区间第k小值

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

  4. A - 低阶入门膜法 - K-th Number (主席树查询区间第k小)

    题目链接:https://cn.vjudge.net/contest/284294#problem/A 题目大意:主席树查询区间第k小. 具体思路:主席树入门. AC代码: #include<i ...

  5. Dynamic Rankings || 动态/静态区间第k小(主席树)

    JYF大佬说,一星期要写很多篇博客才会有人看 但是我做题没有那么快啊QwQ Part1 写在前面 区间第K小问题一直是主席树经典题=w=今天的重点是动态区间第K小问题.静态问题要求查询一个区间内的第k ...

  6. 洛谷P3374(线段树)(询问区间和,支持单点修改)

    洛谷P3374 //询问区间和,支持单点修改 #include <cstdio> using namespace std; ; struct treetype { int l,r,sum; ...

  7. 线段树专题2-(加强版线段树-可持续化线段树)主席树 orz! ------用于解决区间第k大的问题----xdoj-1216

    poj-2104(区间第K大问题) #include <iostream> #include <algorithm> #include <cstdio> #incl ...

  8. ZOJ -2112 Dynamic Rankings 主席树 待修改的区间第K大

    Dynamic Rankings 带修改的区间第K大其实就是先和静态区间第K大的操作一样.先建立一颗主席树, 然后再在树状数组的每一个节点开线段树(其实也是主席树,共用节点), 每次修改的时候都按照树 ...

  9. ZOJ2112 BZOJ1901 Dynamic Rankings 树套树 带修改的区间第k小

    http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=2112 树套树,线段树套splay或者主席树套树状数组,我抄了一下hzwer ...

随机推荐

  1. 监控Linux系统所选的服务所占进程内存占用

    [代码] #!/bin/bash #程序功能描述: # 监控系统所选的服务所占进程内存占用 #作者:孤舟点点 #版本:1.0 #创建时间:-- :: PATH=/bin:/sbin:/usr/bin: ...

  2. Python字符串编码——Unicode

    ASCII码 我们知道,在计算机内部,所有的信息最终都表示为一个二进制的字符串.每一个二进制位(bit)有0和1两种状态,因此八个二进制位就可以组合出256种状态,这被称为一个字节(byte).也就是 ...

  3. windows的各种扩展名详解

    Windows系统文件按照不同的格式和用途分很多种类,为便于管理和识别,在对文件命名时,是以扩展名加以区分的,即文件名格式为: 主文件名.扩展名.这样就可以根据文件的扩展名,判定文件的种类,从而知道其 ...

  4. python爬取许多图片的代码

    from bs4 import BeautifulSoup import requests import os os.makedirs('./img/', exist_ok=True) URL = & ...

  5. 【内推】平安产险大数据测试开发工程师,15-30k!

    平安产险技术岗内部推荐-大数据测试开发工程师等-欢迎中年人和2020应届生 上班地点:深圳福田平安金融中心 另有大量 上海 北京 成都 广州 岗位 交流qq群 828186629 微信pythonte ...

  6. Archlinux 自动挂载移动硬盘,开机自动启动smb服务

    Archlinux + Raspberry 打造NAS: samba篇 树莓派自动挂载硬盘,并开启smb服务. 开机自动挂在移动硬盘ntfs 安装ntfs-3g sudo pacman -S ntfs ...

  7. Coding and Paper Letter(十五)

    资源整理. 1.Nature Climate Change论文"Higher temperatures increase suicide rates in the United States ...

  8. All Tips

    Outlook分享心得 这是在爱奇艺的一场Outlook分享会上我记录的笔记. Read More 分享一点"关于应届生如何写简历"的人生经验 应届生如何写好一份求职简历是一件重要 ...

  9. Web 通信技术 ——跨文档信息传输(JavaScript)

    */ * Copyright (c) 2016,烟台大学计算机与控制工程学院 * All rights reserved. * 文件名:text.html * 作者:常轩 * 微信公众号:Worldh ...

  10. czC#01

    1. .net简介: .net分为.net平台及.net Framework 2..NET作用 2.转义与@ 3.类型转换 1) 隐式转换 2)显式类型转换 (待转换的目标类型)原始值