首先来介绍一下我们需求:给你n个数,多次问你某个区间内的第k小是哪个数

主席树:

主席树的全名应该是 函数式版本的线段树。加上附带的一堆 technology。。

。。总之由于原名字太长了,而且 “主席” 两个字念起来冷艳高贵,以后全部称之为主席树好了。。

主席树的主体是线段树,准确的说,是很多棵线段树,存的是一段数字区间出现次数(所以要先离散化可能出现的数字)。举个例子,假设我每次都要求整个序列内的第 k 小,那么对整个序列构造一个线段树,然后在线段树上不断找第 k 小在当前数字区间的左半部分还是右半部分。这个操作和平衡树的 Rank 操作一样,只是这里将离散的数字搞成了连续的数字。

先假设没有修改操作:

对于每个前缀 S1…i,保存这样一个线段树 Ti,组成主席树。这样不是会 MLE 么?最后再讲。

注意,这个线段树对一条线段,保存的是这个数字区间的出现次数,所以是可以互相加减的!还有,由于每棵线段树都要保存同样的数字,所以它们的大小、形态也都是一样的!这实在是两个非常好的性质,是平衡树所不具备的。

对于询问 (i,j),我只要拿出 Tj 和 Ti-1,对每个节点相减就可以了。说的通俗一点,询问 i..j 区间中,一个数字区间的出现次数时,就是这些数字在 Tj 中出现的次数减去在 Ti-1 中出现的次数。

由于每棵线段树的大小形态都是一样的,而且初始值全都是 0,那每个线段树都初始化不是太浪费了?所以一开始不用建树。

然后是在某棵树上修改一个数字,由于和其他树相关联,所以不能在原来的树上改,必须弄个新的出来。难道要弄一棵新树?不是的,由于一个数字的更改只影响了一条从这个叶子节点到根的路径,所以只要只有这条路径是新的,另外都没有改变。比如对于某个节点,要往右边走,那么左边那些就不用新建,只要用个指针链到原树的此节点左边就可以了,这个步骤的前提也是线段树的形态一样。

所以根据我的理解以及网上的资料:我们首先使用vector进行离散化,每次加一个点就添一条边,并让(1,x(加入的点离散化后的大小))加一,最后模拟线段树查询就好

#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#include<string>
#include<cstdio>
#include<cstring>
#include<iomanip>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define eps 1E-8
/*注意可能会有输出-0.000*/
#define Sgn(x) (x<-eps? -1 :x<eps? 0:1)//x为两个浮点数差的比较,注意返回整型
#define Cvs(x) (x > 0.0 ? x+eps : x-eps)//浮点数转化
#define zero(x) (((x)>0?(x):-(x))<eps)//判断是否等于0
#define mul(a,b) (a<<b)
#define dir(a,b) (a>>b)
typedef long long ll;
typedef unsigned long long ull;
const int Inf=<<;
const ll INF=1ll<<;
const double Pi=acos(-1.0);
const int Mod=1e9+;
const int Max=1e5+;
int root[Max],tot;//存根节点(n个根节点一定不同) 内存池
struct node
{
int lef,rig,date;
} msegtr[Max*];//开得足够大
int a[Max];
vector<int> vec;
int GetId(int num)//找到离散化后原值对应的值
{
return lower_bound(vec.begin(),vec.end(),num)-vec.begin()+;
}
void Create(int sta,int enn,int &x,int y,int pos)//建树添边
{
msegtr[++tot]=msegtr[y];//更新这条边
msegtr[tot].date++;
x=tot;//增加一条边
if(sta==enn)
return;
int mid=dir(sta+enn,);
if(mid>=pos)//左子树添边
Create(sta,mid,msegtr[x].lef,msegtr[y].lef,pos);
else
Create(mid+,enn,msegtr[x].rig,msegtr[y].rig,pos);
return;
}
int Query(int sta,int enn,int x,int y,int k)//查询区间k大,因为满足区间减法
{
if(sta==enn)
{
return sta;
}
int mid=dir(sta+enn,);
int sum=msegtr[msegtr[y].lef].date-msegtr[msegtr[x].lef].date;//左孩子区间的差
if(sum>=k)
return Query(sta,mid,msegtr[x].lef,msegtr[y].lef,k);
else
return Query(mid+,enn,msegtr[x].rig,msegtr[y].rig,k-sum);
}
int main()
{
int n,m,t;
while(~scanf("%d %d",&n,&m))
{
root[]=;
msegtr[].lef=msegtr[].rig=msegtr[].date=;
vec.clear();
tot=;
for(int i=; i<=n; ++i)
{
scanf("%d",&a[i]);
vec.push_back(a[i]);
}
sort(vec.begin(),vec.end());//离散化
vec.erase(unique(vec.begin(),vec.end()),vec.end());//去重
for(int i=; i<=n; ++i)
Create(,n,root[i],root[i-],GetId(a[i]));
int l,r,k;
for(int i=; i<m; ++i)
{
scanf("%d %d %d",&l,&r,&k);
printf("%d\n",vec[Query(,n,root[l-],root[r],k)-]);//返回原来的数字
}
}
return ;
}

参考:http://blog.csdn.net/metalseed/article/details/8045038

主席树----POJ 2104(主席树裸题)(转)的更多相关文章

  1. K-th Number Poj - 2104 主席树

    K-th Number Poj - 2104 主席树 题意 给你n数字,然后有m次询问,询问一段区间内的第k小的数. 解题思路 这个题是限时训练做的题,我不会,看到这个题我开始是拒绝的,虽然题意清晰简 ...

  2. [poj 2104]主席树+静态区间第k大

    题目链接:http://poj.org/problem?id=2104 主席树入门题目,主席树其实就是可持久化权值线段树,rt[i]维护了前i个数中第i大(小)的数出现次数的信息,通过查询两棵树的差即 ...

  3. [划分树] POJ 2104 K-th Number

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

  4. K-th Number POJ - 2104 划分树

    K-th Number You are working for Macrohard company in data structures department. After failing your ...

  5. POJ 1087 最大流裸题 + map

    A Plug for UNIX Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 15597   Accepted: 5308 ...

  6. POJ 2104 主席树模板题

    #include <iostream> #include <cstdio> #include <algorithm> int const maxn = 200010 ...

  7. poj 2104 主席树(区间第k大)

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

  8. POJ 2104 - 主席树 / 询问莫队+权值分块

    传送门 题目大意应该都清楚. 今天看到一篇博客用分块+莫对做了这道题,直接惊呆了. 首先常规地离散化后将询问分块,对于某一询问,将莫队指针移动到指定区间,移动的同时处理权值分块的数字出现次数(单独.整 ...

  9. poj 2104 划分树

    思路:裸的划分树 #include<iostream> #include<algorithm> #include<cstring> #include<cstd ...

随机推荐

  1. C# MD5加密与校验 引用

    using System; using System.Security.Cryptography; using System.Text; class Example { // Hash an inpu ...

  2. Quality of service

    w https://en.wikipedia.org/wiki/Quality_of_service Quality of service (QoS) is the overall performan ...

  3. java 对象与二进制互转

    来自 : http://blog.csdn.net/luckyzhoustar/article/details/50402427 /** * @FileName: ByteToObject.java ...

  4. 0x08 MySQL 超详细-索引原理&慢查询优化【转-多图】(重点)

    Content From——Egon's Blog http://www.cnblogs.com/linhaifeng/articles/7274563.html#top 0x01 介绍 为何要有索引 ...

  5. Python中的魔术(双下划线'__xxx__')方法详解

    介绍 在Python中,所有以“__”双下划线包起来的方法,都统称为“Magic Method”,中文称『魔术方法』,例如类的初始化方法 __init__ ,Python中所有的魔术方法均在官方文档中 ...

  6. PAT 1072. 开学寄语(20) JAVA

    下图是上海某校的新学期开学寄语:天将降大任于斯人也,必先删其微博,卸其QQ,封其电脑,夺其手机,收其ipad,断其wifi,使其百无聊赖,然后,净面.理发.整衣,然后思过.读书.锻炼.明智.开悟.精进 ...

  7. 笔画宽度变化(C++和matlab算法)

    最近一直在看工作方面的书籍,把论文的事情搁置了,之前承诺的贴代码的事一直拖.现在把代码整理发上来,只有核心部分的,都不是我写的,我是网上整理下载的,matlab代码的效果比较差. 全部文件网盘下载地址 ...

  8. ABAP锁,数据库锁

    原文出自 江正军 技术博客,博客链接:www.cnblogs.com/jiangzhengjun ABAP数据锁定 SM12锁查看与维护 通用加锁与解锁函数 ABAP程序锁定 数据库锁 锁的分类和兼容 ...

  9. when you are old

    When you are old william Butler Yeats When you are old and grey and full of sleep And nodding by the ...

  10. Nginx -HTTP和反向代理服务器简单配置

    from:http://blog.sina.com.cn/s/blog_6b64b6a701011feh.html Nginx官方测试能够支持5万并发连接,实际生产环境中可支持2-4万并发连接数. 在 ...