POJ - 2104 K-th Number(主席树)
2017-09-03 19:52
441 查看
点我看题
题意:给出一个序列,然后求其某个区间内的第k大值。
分析:主席树的模板题,这题不涉及到修改操作,只有查询。
先来看看主席树是什么。
主席树,也就是可持久化的线段树,又称函数式线段树。(看到知乎上对主席树和可持久化线段树有一些区分
那么,可持久化是什么意思呢,目前认为就是去利用前面已经知道的一些数据来减少当前对空间的消耗,通俗的讲,就是我之前已经知道那些值是什么了,那么我现在还要想知道那些值,就直接指向前面就好,不用特意记下来。
主席树的结构就是n+1棵线段树,第i棵线段树记录前i个数的情况。
那么对于这个题来说,我们也可以用主席树求解。
参考代码:
/*主席树*/
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int maxn = 1e5+10;
int n,q,m;
int a[maxn],hs[maxn];//a为原序列,hs为对a进行hash后的结果
//主席树
int tot;//总结点数
int pt[maxn<<5],lson[maxn<<5],rson[maxn<<5],c[maxn<<5];//结点值,左右儿子,结点对应区间内存在的数的个数
//对原序列做一个哈希,哈希函数为f(x) = x;0
//m为哈希后的序列长度
void InitHash()
{
for( int i = 1; i <= n; i++)
hs[i] = a[i];
sort(hs+1,hs+1+n);
m = unique(hs+1,hs+1+n)-hs-1;//去重
}
//建树,这里建的是一颗“空树”
int Build( int l, int r)
{
int rt = tot++;//根节点序号
c[rt] = 0;//结点代表区间含有的数的个数
if( l != r)//非叶子结点
{
int mid = (l+r)>>1;
lson[rt] = Build(l,mid);//建左右子树
rson[rt] = Build(mid+1,r);
}
return rt;//返回记录下根节点的值
}
//哈希查找,在hs数组中寻找x的位置
int Hash( int x)
{
return lower_bound(hs+1,hs+1+m,x)-hs;
}
//更新操作,本题中每一次更新都像是新建一棵树
int Update( int rt, int pos, int val)
{
//rt为上一棵树的根节点
int newrt = tot++;;//根节点
int tmp = newrt;//,临时记录下
c[newrt] = c[rt]+val;//该结点对应区间的值多了val个,更新个数
int l = 1, r = m;
while( l < r)
{
int mid = (l+r)>>1;
if( pos <= mid)//更新的为值在左子树
{
lson[newrt] = tot++;//新建左子树
rson[newrt] = rson[rt];//右子树就直接指向前面已知的相同区间(可持久化)
newrt = lson[newrt];
rt = lson[rt];//rt指向自己的左子树
r = mid;
}
else//更新的为右子树
{
rson[newrt] = tot++;//同理,新建右子树
lson[newrt] = lson[rt];//左子树指向已存在的
newrt = rson[newrt];//
rt = rson[rt];
l = mid+1;
}
c[newrt] = c[rt]+val;//更新对应个数
}
return tmp;//返回答案
}
//总的大左右区间
int Query( int lrt, int rrt, int k)
{
int l = 1, r = m;
while( l < r)
{
int mid = (l+r)>>1;
if( c[lson[lrt]]-c[lson[rrt]] >= k)//大于k的话肯定在左边
{
r = mid;
lrt = lson[lrt];
rrt = lson[rrt];
}
else//反之,在右边
{
l = mid+1;
k -= c[lson[lrt]]-c[lson[rrt]];
lrt = rson[lrt];
rrt = rson[rrt];
}
}
return l;
}
int main()
{
while( ~scanf("%d%d",&n,&q))
{
for( int i = 1; i <= n; i++)
scanf("%d",&a[i]);
//InitHash
InitHash();
tot = 0;
pt[n+1] = Build(1,m);
for( int i = n; i ; i--)
{
int pos = Hash(a[i]);
pt[i] = Update(pt[i+1],pos,1);
}
while( q--)
{
int l,r,k;
scanf("%d%d%d",&l,&r,&k);
printf("%d\n",hs[Query(pt[l],pt[r+1],k)]);
}
}
return 0;
}
题意:给出一个序列,然后求其某个区间内的第k大值。
分析:主席树的模板题,这题不涉及到修改操作,只有查询。
先来看看主席树是什么。
主席树,也就是可持久化的线段树,又称函数式线段树。(看到知乎上对主席树和可持久化线段树有一些区分
那么,可持久化是什么意思呢,目前认为就是去利用前面已经知道的一些数据来减少当前对空间的消耗,通俗的讲,就是我之前已经知道那些值是什么了,那么我现在还要想知道那些值,就直接指向前面就好,不用特意记下来。
主席树的结构就是n+1棵线段树,第i棵线段树记录前i个数的情况。
那么对于这个题来说,我们也可以用主席树求解。
参考代码:
/*主席树*/
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int maxn = 1e5+10;
int n,q,m;
int a[maxn],hs[maxn];//a为原序列,hs为对a进行hash后的结果
//主席树
int tot;//总结点数
int pt[maxn<<5],lson[maxn<<5],rson[maxn<<5],c[maxn<<5];//结点值,左右儿子,结点对应区间内存在的数的个数
//对原序列做一个哈希,哈希函数为f(x) = x;0
//m为哈希后的序列长度
void InitHash()
{
for( int i = 1; i <= n; i++)
hs[i] = a[i];
sort(hs+1,hs+1+n);
m = unique(hs+1,hs+1+n)-hs-1;//去重
}
//建树,这里建的是一颗“空树”
int Build( int l, int r)
{
int rt = tot++;//根节点序号
c[rt] = 0;//结点代表区间含有的数的个数
if( l != r)//非叶子结点
{
int mid = (l+r)>>1;
lson[rt] = Build(l,mid);//建左右子树
rson[rt] = Build(mid+1,r);
}
return rt;//返回记录下根节点的值
}
//哈希查找,在hs数组中寻找x的位置
int Hash( int x)
{
return lower_bound(hs+1,hs+1+m,x)-hs;
}
//更新操作,本题中每一次更新都像是新建一棵树
int Update( int rt, int pos, int val)
{
//rt为上一棵树的根节点
int newrt = tot++;;//根节点
int tmp = newrt;//,临时记录下
c[newrt] = c[rt]+val;//该结点对应区间的值多了val个,更新个数
int l = 1, r = m;
while( l < r)
{
int mid = (l+r)>>1;
if( pos <= mid)//更新的为值在左子树
{
lson[newrt] = tot++;//新建左子树
rson[newrt] = rson[rt];//右子树就直接指向前面已知的相同区间(可持久化)
newrt = lson[newrt];
rt = lson[rt];//rt指向自己的左子树
r = mid;
}
else//更新的为右子树
{
rson[newrt] = tot++;//同理,新建右子树
lson[newrt] = lson[rt];//左子树指向已存在的
newrt = rson[newrt];//
rt = rson[rt];
l = mid+1;
}
c[newrt] = c[rt]+val;//更新对应个数
}
return tmp;//返回答案
}
//总的大左右区间
int Query( int lrt, int rrt, int k)
{
int l = 1, r = m;
while( l < r)
{
int mid = (l+r)>>1;
if( c[lson[lrt]]-c[lson[rrt]] >= k)//大于k的话肯定在左边
{
r = mid;
lrt = lson[lrt];
rrt = lson[rrt];
}
else//反之,在右边
{
l = mid+1;
k -= c[lson[lrt]]-c[lson[rrt]];
lrt = rson[lrt];
rrt = rson[rrt];
}
}
return l;
}
int main()
{
while( ~scanf("%d%d",&n,&q))
{
for( int i = 1; i <= n; i++)
scanf("%d",&a[i]);
//InitHash
InitHash();
tot = 0;
pt[n+1] = Build(1,m);
for( int i = n; i ; i--)
{
int pos = Hash(a[i]);
pt[i] = Update(pt[i+1],pos,1);
}
while( q--)
{
int l,r,k;
scanf("%d%d%d",&l,&r,&k);
printf("%d\n",hs[Query(pt[l],pt[r+1],k)]);
}
}
return 0;
}
相关文章推荐
- POJ 2104 K-th Number (划分树,主席树写过了,这次是整体二分解法 )
- POJ 2104 K-th Number 静态主席树(裸
- [POJ 2104]K-th Number【模板】(主席树)
- POJ-2104:K-th Number(主席树)
- poj 2104 K-th Number 主席树+超级详细解释
- 主席树(可持久化线段树)讲解 [POJ 2104] K-th Number
- poj 2104 K-th Number (主席树)
- 【POJ 2104】(K-th Number-区间第k大-主席树)
- POJ 2104 & HDU 2665 & POJ 2761 K-th Number (主席树入门题 区间第K大)
- POJ 2104 K-th Number (主席树)
- poj 2104 K-th Number (静态区间第k大,主席树)
- poj2104 K-th number 主席树模版
- 【POJ 2104/HDU 2665】K-th Number【整体二分/主席树】
- Poj2104-K-th Number(主席树)
- POJ 2104 K-th Number(主席树)
- POJ 2104(K-th Number-区间第k大-主席树)
- POJ 2104:K-th Number(主席树)
- 【POJ】2104 K-th Number(区间k大+主席树)
- POJ 2104 K-th Number 主席树
- poj 2104 K-th Number【主席树】