POJ2104 K-TH NUMBER 传说中的主席树
题意:给出1e5个数,向你提5000问,L到R间的第K个数是多少?
————————————————————————————————————————————————————————
这个题着实没有其它很好的办法,是主席树的经典题目。
主席树,又叫可持久化线段树或者函数式线段树。我只知道这些了,据说是一位大神没学会划分树就自己搞了这么个替代品,结果,比原来的还要强。哎~,真不知道这群大神的脑子是咋整的,膜拜吧!
本人水平有限,这是我见过的最神奇的数据结构了,感觉比伸展树、熟练剖分神奇多了,鼓捣了大半天才明白其中的原理。
说一下思路:
因为是求第k大的数,所以线段树是不行的,因为数是排好序的,所以伸展树好像也不好解决。
所以主席树用到了前缀和的思想,如果知道每个数在L前出现了几次,R前出现了几次,做差就可以在L~R间出现了几次,自然也就知道k大了!
所以,第一步把所有的数排序、去重。
数列:2 4 6 8 8 6 4 2
hash:2 4 6 8
数列变为:1 2 3 4 4 3 2 1(就是各个数在hash中的排序)
第二部建n+1个线段树,就是按照数列的顺序每读入一个数就建一个线段树,把新树在上一颗树的基础上把这个数字按照去重后的排序加入当前线段树。
这样求L~R的k值就用线段树R减去线段树L-1,在差中找k大值就可以了。



......
出现的问题:超空间
这个事大神替我们想到了,那就是每一个线段树与上一个线段树只有一条路径(根到叶)不同,所以,新树除了这条路径外所有节点都用上一个线段树的。
这样空间为第一颗线段树(4*n)+每个数字一条链(n*logn)

代码敲了1天多,主要是本人习惯于用指针和new,但是这次点太多,超时了。没办法只好给出数组模拟指针。ok!
网上有人说可以一次性malloc多个,这样可以省时通过,没来得及试,不过应该可以,但是就没有了指针+new的灵活性,不做也罢。
但是没舍得把指针的丢掉,这个理解起来清楚容易,哎~,就这麽着吧!
————————————————————————————————————————————————————————
数组版:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm> using namespace std;
const int maxn=;
const int maxnn=;
int root[maxn],ls[maxnn],rs[maxnn],cnt[maxnn],tot;
int sz[maxn],hash[maxn];
void build(int &cur,int l,int r)
{
cur=tot++;
cnt[cur]=;
if(l!=r)
{
int mid=(l+r)/;
build(ls[cur],l,mid);
build(rs[cur],mid+,r);
}
}
void update(int pre,int ps,int &cur,int l,int r)
{
cur=tot++;
cnt[cur]=cnt[pre]+;
ls[cur]=ls[pre];rs[cur]=rs[pre];
if(l==r)return ;
int mid=(l+r)/;
if(ps<=mid)update(ls[pre],ps,ls[cur],l,mid);
else update(rs[pre],ps,rs[cur],mid+,r);
}
int query(int lt,int rt,int l,int r,int k)
{
if(l==r)return l;
int mid=(l+r)/,cha=cnt[ls[rt]]-cnt[ls[lt]];
if(k<=cha)return query(ls[lt],ls[rt],l,mid,k);
else return query(rs[lt],rs[rt],mid+,r,k-cha);
}
int main()
{
int m,n,l,r,k;
while(scanf("%d%d",&n,&m)==)
{
for(int i=;i<=n;++i)
{
scanf("%d",sz+i);
hash[i]=sz[i];
}
sort(hash+,hash+n+);
int siz=unique(hash+,hash++n)-hash-;
for(int i=;i<=n;++i)
sz[i]=lower_bound(hash+,hash++siz,sz[i])-hash;
tot=;
build(root[],,siz);
for(int i=;i<=n;++i)
update(root[i-],sz[i],root[i],,siz);
while(m--)
{
scanf("%d%d%d",&l,&r,&k);
printf("%d\n",hash[query(root[l-],root[r],,siz,k)]);
}
}
return ;
}
指针版(由于多次malloc,超时了)
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=;
struct node
{
int cnt;
node *ch[];
int l,r;
}* root[maxn];
int n,mm,l,r,k;
int sz[maxn],hash[maxn]; void readint(int &x)
{
char c=getchar();
int f=;
for(;c<''||c>'';c=getchar())if(c=='-')f=-f;
x=;
for(;c<=''&&c>='';c=getchar())x=x*+c-'';
x=x*f;
}
void build(node * &cur,int l,int r)
{
cur=(node *)malloc(sizeof(node));
cur->cnt=;
cur->ch[]=cur->ch[]=NULL;
cur->l=l;cur->r=r;
if(l!=r)
{
int mid=(l+r)/;
build(cur->ch[],l,mid);
build(cur->ch[],mid+,r);
}
}
void update(node * pre,int ps,node * &cur,int l,int r)
{
cur=(node *)malloc(sizeof(node));
cur->cnt=pre->cnt+;
cur->l=pre->l;cur->r=pre->r;
cur->ch[]=pre->ch[];
cur->ch[]=pre->ch[];
if(l==r)return ;
int mid=(l+r)/;
if(ps<=mid)update(pre->ch[],ps,cur->ch[],l,mid);
else update(pre->ch[],ps,cur->ch[],mid+,r);
}
int query(node * lt,node *rt,int l,int r,int k)
{
if(l==r)return l;
int mid=(l+r)/,cha=rt->ch[]->cnt - lt->ch[]->cnt;
if(k<=cha)return query(lt->ch[],rt->ch[],l,mid,k);
else return query(lt->ch[],rt->ch[],mid+,r,k-cha);
}
int main()
{
while(scanf("%d%d",&n,&mm)==)
{
for(int i=;i<=n;++i)
{
readint(sz[i]);
hash[i]=sz[i];
}
sort(hash+,hash+n+);
int siz=unique(hash+,hash+n+)-hash-;
for(int i=;i<=n;++i)
sz[i]=lower_bound(hash+,hash+siz+,sz[i])-hash;
build(root[],,siz);
for(int i=;i<=n;++i)
update(root[i-],sz[i],root[i],,siz);
while(mm--)
{
readint(l);readint(r);readint(k);
printf("%d\n",hash[query(root[l-],root[r],,siz,k)]);
}
}
return ;
}
POJ2104 K-TH NUMBER 传说中的主席树的更多相关文章
- [POJ2104] K – th Number (可持久化线段树 主席树)
题目背景 这是个非常经典的主席树入门题--静态区间第K小 数据已经过加强,请使用主席树.同时请注意常数优化 题目描述 如题,给定N个正整数构成的序列,将对于指定的闭区间查询其区间内的第K小值. 输入输 ...
- K-th Number Poj - 2104 主席树
K-th Number Poj - 2104 主席树 题意 给你n数字,然后有m次询问,询问一段区间内的第k小的数. 解题思路 这个题是限时训练做的题,我不会,看到这个题我开始是拒绝的,虽然题意清晰简 ...
- BZOJ_4026_dC Loves Number Theory _主席树+欧拉函数
BZOJ_4026_dC Loves Number Theory _主席树+欧拉函数 Description dC 在秒了BZOJ 上所有的数论题后,感觉萌萌哒,想出了这么一道水题,来拯救日益枯 竭 ...
- 主席树总结(经典区间第k小问题)(主席树,线段树)
接着上一篇总结--可持久化线段树来整理吧.点击进入 这两种数据结构确实有异曲同工之妙.结构是很相似的,但维护的主要内容并不相同,主席树的离散化.前缀和等思想也要更难理解一些. 闲话 话说刚学习主席树的 ...
- bzoj : 4504: K个串 区间修改主席树
4504: K个串 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 268 Solved: 110[Submit][Status][Discuss] ...
- [poj2104] K-th Number (主席树)
主席树 Description You are working for Macrohard company in data structures department. After failing y ...
- 可持久化线段树(主席树)(图文并茂详解)【poj2104】【区间第k大】
[pixiv] https://www.pixiv.net/member_illust.php?mode=medium&illust_id=63740442 向大(hei)佬(e)实力学(di ...
- POJ 2104:K-th Number(主席树静态区间k大)
题目大意:对于一个序列,每次询问区间[l,r]的第k大树. 分析: 主席树模板题 program kthtree; type point=record l,r,s:longint; end; var ...
- 主席树[可持久化线段树](hdu 2665 Kth number、SP 10628 Count on a tree、ZOJ 2112 Dynamic Rankings、codeforces 813E Army Creation、codeforces960F:Pathwalks )
在今天三黑(恶意评分刷上去的那种)两紫的智推中,突然出现了P3834 [模板]可持久化线段树 1(主席树)就突然有了不详的预感2333 果然...然后我gg了!被大佬虐了! hdu 2665 Kth ...
随机推荐
- AdapterPattern(适配器模式)
import org.omg.PortableServer.AdapterActivator; /** * 分两种情况: * 1.类适配器 * 2.对象适配器 * 作用:让原本接口不兼容的两个类可以在 ...
- 24、ASP.NET MVC入门到精通——数据库仓储
本系列目录:ASP.NET MVC4入门到精通系列目录汇总 业务层调用数据层对象,我不想每次都new一个数据层对象,而是在数据层创建一个仓储,统一管理所有的对象调用. 1.在IDAL项目中,新建IDB ...
- jQuery:年月日三级联动
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default3.aspx.cs ...
- [转载]C#使用Interlocked进行原子操作
原文链接:王旭博客 » C# 使用Interlocked进行原子操作 什么是原子操作? 原子(atom)本意是"不能被进一步分割的最小粒子",而原子操作(atomic operat ...
- mysql NOW,CURRENT_TIMESTAMP,SYSDATE 之间的区别
这些函数都可以返回当前的系统时间,但它们之间有什么区别呢??大家先看一下以下这个例子. select NOW(), CURRENT_TIMESTAMP(),SYSDATE(); 从上面的例子可以看出返 ...
- jQuery $(document).ready()和JavaScript onload事件
jQuery $(document).ready()和JavaScript onload事件 Why we need a right time? 对元素的操作和事件的绑定需要等待一个合适的时机,可以看 ...
- iOS 开发中的争议(一)
序言 打算分享一些有争议的话题,并且表达一下我的看法.这是该系列的第一篇,我想讨论的是:类的成员变量应该如何定义? 在 Objective-C 的语言的早期,类的私有成员变量是只能定义在 .h 的头文 ...
- ORA-14450: attempt to access a transactional temp table already in use
在ORACLE数据中修改会话级临时表时,有可能会遇到ORA-14550错误,那么为什么会话级全局临时表会报ORA-14450错误呢,如下所示,我们先从一个小小案例入手: 案例1: SQL> CR ...
- android 让一个Activity停留几秒后再跳转
有时候我们需要在某个 Activity 停留几秒种,然后再跳到下一个 Activity.那么这个怎么实现呢? 一个方法是可以使用 Thread 的 sleep 函数,这个我们在 用Handler实现线 ...
- Linux非root用户如何使用80端口启动程序
默认情况下Linux的1024以下端口是只有root用户才有权限占用,我们的tomcat,apache,nginx等等程序如果想要用普通用户来占用80端口的话就会抛出java.net.BindExce ...