洛谷3834 hdu2665主席树模板,动态查询区间第k小
题目链接: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存状态,主席树的代码还是非常简单的,笔者为了让读者更好地理解,写了详细的注释:
- #include<bits/stdc++.h>
- using namespace std;
- typedef unsigned int ui;
- typedef long long ll;
- typedef unsigned long long ull;
- #define pf printf
- #define mem(a,b) memset(a,b,sizeof(a))
- #define prime1 1e9+7
- #define prime2 1e9+9
- #define pi 3.14159265
- #define lson l,mid,rt<<1
- #define rson mid+1,r,rt<<1|1
- #define scand(x) scanf("%llf",&x)
- #define f(i,a,b) for(int i=a;i<=b;i++)
- #define scan(a) scanf("%d",&a)
- #define mp(a,b) make_pair((a),(b))
- #define P pair<int,int>
- #define dbg(args) cout<<#args<<":"<<args<<endl;
- #define inf 0x3f3f3f3f
- const int maxn=2e5+;
- int n,m;
- int a[maxn];
- vector<int>v;//离散化之后将内容存储在vector中
- int getid(int x)
- {
- return lower_bound(v.begin(),v.end(),x)-v.begin()+;//下标是从1开始直到n结束
- }
- struct node{
- int l,r,sum;
- //分别保存左右子树的根节点的编号以及当前结点的数值,
- //在还没插入点信息的时候子树中每个结点的值都是0,所以主席树不需要建树,是边插入边建树的
- }t[maxn*]; //适应O(nlogn)空间需求,2^40次方大小的数据是不可能的,所以可以根据习惯进行选择
- int cnt=,root[maxn];
- //权值线段树中插入的值p就是插到位置p ,由于当前结点是需要变化的,所以传入引用使他指向内存池中新的结点
- void insert(int l, int r,int pre,int& now,int p)//参数中有前面一棵树的结点以及当前树的结点,
- {
- t[++cnt]=t[pre];//从内存池中生成一个根结点并将前面树的根节点复制到其中,
- now=cnt; //使当前结点指向新生成的根结点,
- t[now].sum++;
- //要在第p位上插入,所以当前访问的结点是一定是增1的,
- //后面我们将会决定访问左子树的结点还是右子树的结点
- if(l==r)return;//到达了点信息而且前面已经在叶子结点上面更新过点信息,所以直接return
- int m=l+r>>;
- //如果左子树的区间包括了p点就向左子树递归,否则走右子树,同时也要分别移动到左子树和右子树
- if(p<=m)insert(l,m,t[pre].l,t[now].l,p);
- else insert(m+,r,t[pre].r,t[now].r,p);
- }
- int query(int l,int r,int L,int R,int k)//两个结点同步相减
- {
- if(l==r)return l;//返回的是点信息,就是离散化之后的坐标,便于之后通过vector进行索引
- int m=l+r>>;
- int tmp=t[t[R].l].sum-t[t[L].l].sum;//先获取两棵树左子树的键值之差决定向哪一棵数递归
- if(k<=tmp)return query(l,m,t[L].l,t[R].l,k);
- else return query(m+,r,t[L].r,t[R].r,k-tmp);//如果tmp<k,就查询右子树中的第k-tmp小的数,有点分治的意味
- }
- int main()
- {
- //freopen("input.txt","r",stdin);
- //freopen("output.txt","w",stdout);
- std::ios::sync_with_stdio(false);
- scan(n);
- scan(m);
- f(i,,n)
- {
- scan(a[i]);
- v.push_back(a[i]);
- }
- sort(v.begin(),v.end());
- //将数列先排序再去重放入vector中以便通过位置获取在线段树中代表的区间点信息
- v.erase(unique(v.begin(),v.end()),v.end());
- //unique函数将不重复元素放到vector的前部,
- //返回的时所有不重复元素的下一个位置,所以将后面的元素删去就可以离散的成为线段树的坐标点信息
- f(i,,n)
- {
- insert(,n,root[i-],root[i],getid(a[i]));//在第i-1棵树上建第i棵树,所以传入的是两棵树的根节点
- }
- int l,r,k;
- while(m--)
- {
- scanf("%d%d%d",&l,&r,&k);
- pf("%d\n",v[query(,n,root[l-],root[r],k)-]);
- //查询[l,r]区间更新的信息,实现动态查询第k小,
- //注意vector中下标是从0开始的,而线段树中下标是从1开始的,所以错位了1,为了达到原来的元素就要减一
- }
- }
hdu2665 Kth number:
- #include<bits/stdc++.h>
- using namespace std;
- typedef unsigned int ui;
- typedef long long ll;
- typedef unsigned long long ull;
- #define pf printf
- #define mem(a,b) memset(a,b,sizeof(a))
- #define prime1 1e9+7
- #define prime2 1e9+9
- #define pi 3.14159265
- #define lson l,mid,rt<<1
- #define rson mid+1,r,rt<<1|1
- #define scand(x) scanf("%llf",&x)
- #define f(i,a,b) for(int i=a;i<=b;i++)
- #define scan(a) scanf("%d",&a)
- #define mp(a,b) make_pair((a),(b))
- #define P pair<int,int>
- #define dbg(args) cout<<#args<<":"<<args<<endl;
- #define inf 0x3f3f3f3f
- const int maxn=2e5+;
- int n,m,q;
- vector<int> v;
- int cnt,root[maxn];
- int getid(int x)
- {
- return lower_bound(v.begin(),v.end(),x)-v.begin()+;
- }
- int a[maxn];
- struct node{
- int l,r,sum;
- }t[maxn*];
- void insert(int l,int r,int pre,int &now,int pos)
- {
- t[++cnt]=t[pre];
- now=cnt;
- t[now].sum++;
- if(l==r)return;
- int m=l+r>>;
- if(pos<=m)insert(l,m,t[pre].l,t[now].l,pos);
- else insert(m+,r,t[pre].r,t[now].r,pos);
- }
- int query(int l,int r,int L,int R,int k)
- {
- if(l==r)return l;
- int tmp=t[t[R].l].sum-t[t[L].l].sum;
- int m=l+r>>;
- if(k<=tmp)return query(l,m,t[L].l,t[R].l,k);
- else return query(m+,r,t[L].r,t[R].r,k-tmp);
- }
- int main()
- {
- //freopen("input.txt","r",stdin);
- //freopen("output.txt","w",stdout);
- std::ios::sync_with_stdio(false);
- scan(q);
- f(kk,,q)
- {
- scan(n);scan(m);
- v.clear();
- cnt=;
- mem(root,);
- f(i,,n)
- {
- scan(a[i]);
- v.push_back(a[i]);
- }
- sort(v.begin(),v.end());
- v.erase(unique(v.begin(),v.end()),v.end());
- f(i,,n)insert(,n,root[i-],root[i],getid(a[i]));
- int l,r,k;
- f(tt,,m)
- {
- scanf("%d%d%d",&l,&r,&k);
- pf("%d",v[query(,n,root[l-],root[r],k)-]);
- pf("\n");
- }
- }
- }
洛谷3834 hdu2665主席树模板,动态查询区间第k小的更多相关文章
- poj 2761 主席树的应用(查询区间第k小值)
Feed the dogs Time Limit: 6000MS Memory Limit: 65536K Total Submissions: 22084 Accepted: 7033 De ...
- 主席树总结(经典区间第k小问题)(主席树,线段树)
接着上一篇总结--可持久化线段树来整理吧.点击进入 这两种数据结构确实有异曲同工之妙.结构是很相似的,但维护的主要内容并不相同,主席树的离散化.前缀和等思想也要更难理解一些. 闲话 话说刚学习主席树的 ...
- 【可持久化线段树】POJ2104 查询区间第k小值
K-th Number Time Limit: 20000MS Memory Limit: 65536K Total Submissions: 61284 Accepted: 21504 Ca ...
- A - 低阶入门膜法 - K-th Number (主席树查询区间第k小)
题目链接:https://cn.vjudge.net/contest/284294#problem/A 题目大意:主席树查询区间第k小. 具体思路:主席树入门. AC代码: #include<i ...
- Dynamic Rankings || 动态/静态区间第k小(主席树)
JYF大佬说,一星期要写很多篇博客才会有人看 但是我做题没有那么快啊QwQ Part1 写在前面 区间第K小问题一直是主席树经典题=w=今天的重点是动态区间第K小问题.静态问题要求查询一个区间内的第k ...
- 洛谷P3374(线段树)(询问区间和,支持单点修改)
洛谷P3374 //询问区间和,支持单点修改 #include <cstdio> using namespace std; ; struct treetype { int l,r,sum; ...
- 线段树专题2-(加强版线段树-可持续化线段树)主席树 orz! ------用于解决区间第k大的问题----xdoj-1216
poj-2104(区间第K大问题) #include <iostream> #include <algorithm> #include <cstdio> #incl ...
- ZOJ -2112 Dynamic Rankings 主席树 待修改的区间第K大
Dynamic Rankings 带修改的区间第K大其实就是先和静态区间第K大的操作一样.先建立一颗主席树, 然后再在树状数组的每一个节点开线段树(其实也是主席树,共用节点), 每次修改的时候都按照树 ...
- ZOJ2112 BZOJ1901 Dynamic Rankings 树套树 带修改的区间第k小
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=2112 树套树,线段树套splay或者主席树套树状数组,我抄了一下hzwer ...
随机推荐
- 监控Linux系统所选的服务所占进程内存占用
[代码] #!/bin/bash #程序功能描述: # 监控系统所选的服务所占进程内存占用 #作者:孤舟点点 #版本:1.0 #创建时间:-- :: PATH=/bin:/sbin:/usr/bin: ...
- Python字符串编码——Unicode
ASCII码 我们知道,在计算机内部,所有的信息最终都表示为一个二进制的字符串.每一个二进制位(bit)有0和1两种状态,因此八个二进制位就可以组合出256种状态,这被称为一个字节(byte).也就是 ...
- windows的各种扩展名详解
Windows系统文件按照不同的格式和用途分很多种类,为便于管理和识别,在对文件命名时,是以扩展名加以区分的,即文件名格式为: 主文件名.扩展名.这样就可以根据文件的扩展名,判定文件的种类,从而知道其 ...
- python爬取许多图片的代码
from bs4 import BeautifulSoup import requests import os os.makedirs('./img/', exist_ok=True) URL = & ...
- 【内推】平安产险大数据测试开发工程师,15-30k!
平安产险技术岗内部推荐-大数据测试开发工程师等-欢迎中年人和2020应届生 上班地点:深圳福田平安金融中心 另有大量 上海 北京 成都 广州 岗位 交流qq群 828186629 微信pythonte ...
- Archlinux 自动挂载移动硬盘,开机自动启动smb服务
Archlinux + Raspberry 打造NAS: samba篇 树莓派自动挂载硬盘,并开启smb服务. 开机自动挂在移动硬盘ntfs 安装ntfs-3g sudo pacman -S ntfs ...
- Coding and Paper Letter(十五)
资源整理. 1.Nature Climate Change论文"Higher temperatures increase suicide rates in the United States ...
- All Tips
Outlook分享心得 这是在爱奇艺的一场Outlook分享会上我记录的笔记. Read More 分享一点"关于应届生如何写简历"的人生经验 应届生如何写好一份求职简历是一件重要 ...
- Web 通信技术 ——跨文档信息传输(JavaScript)
*/ * Copyright (c) 2016,烟台大学计算机与控制工程学院 * All rights reserved. * 文件名:text.html * 作者:常轩 * 微信公众号:Worldh ...
- czC#01
1. .net简介: .net分为.net平台及.net Framework 2..NET作用 2.转义与@ 3.类型转换 1) 隐式转换 2)显式类型转换 (待转换的目标类型)原始值