[POJ2104]K-th Number(主席树+讲解+可持久化基础)

本文详细介绍了主席树(可持久化线段树)的基本概念、实现原理及应用场景,并通过一个具体的裸题实例演示了如何使用主席树解决区间第k大的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:

我是超链接

题解:

主席树裸题?下方普及向见咯

代码:

#include <cstdio>
#include <algorithm>
using namespace std;
const int N=100005;
int root[N],a[N],b[N],p[N],sz,s;
struct hh{int l,r,w;}tree[N*80];
int cmp(int x,int y){return a[x]<a[y];}
void insert(int &now,int l,int r,int x)
{
    tree[++sz]=tree[now]; now=sz;
    tree[now].w++;
    if (l==r) return;
    int mid=(l+r)>>1;
    if (x<=mid) insert(tree[now].l,l,mid,x);
    else insert(tree[now].r,mid+1,r,x);
}

int qurry(int i,int j,int l,int r,int k)
{
    if (l==r) return l;
    int t=tree[tree[j].l].w-tree[tree[i].l].w;
    int mid=(l+r)>>1;
    if (t>=k) return qurry(tree[i].l,tree[j].l,l,mid,k);
    else return qurry(tree[i].r,tree[j].r,mid+1,r,k-t);
}
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]),b[++s]=a[i];
    sort(b+1,b+s+1);
    s=unique(b+1,b+s+1)-b-1;
    for (int i=1;i<=n;i++) p[i]=lower_bound(b+1,b+s+1,a[i])-b;
    sz=0; root[0]=0;
    for (int i=1;i<=n;i++)
    {
        root[i]=root[i-1];
        insert(root[i],1,n,p[i]);
    }
    while (m--)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        int t=qurry(root[x-1],root[y],1,n,z);
        printf("%d\n",b[t]);
    }
}

普及向:

喵喵喵竟然发现自己还不知道什么是可持久化。。。

 可持久数据结构主要指的是我们可以查询历史版本的情况并支持插入,利用使用之前历史版本的数据结构来减少对空间的消耗(能够对历史进行修改的是函数式)。

一般的实现方法是新建有修改的点 其他点与上一版本共用 这样做到空间复杂度带上一个或两个log
比较常用的—–可持久化线段树(主席树)+可持久化trie树

然后喵喵喵就发现自己对于主席树的程度只有默写板子,任重而道远!
主席树(可持久化线段树,函数式线段树)从区间第k大的问题入手,这可是裸题呀,推荐一波优秀的up

时间复杂度:构造O(nlogn),单次查询O(logn)

那么重点就是,每个节点建一棵区间[1,n]的线段树,每个节点存储的是在该区间内出现的数字的个数,这里的每一棵线段树的区间就是数字大小的区间
我还是举个栗子吧
这里写图片描述
这里写图片描述
这里写图片描述

我们可以发现每一步到下一步的时候大部分的节点都是一样的,我们就把ta直接拉过来,实现可持久化,最优秀的拉拢方法是:单独修改要修改的路,被修改的肯定是新编号的节点,除了这条路径上的承接自父亲,父亲承接自上一个根节点
至于扩展到一个区间[x,y]就[1,x],[1,y]对应的两个圈内的数字相减就好了
想要查询区间第k大的时候,用类似splay的find操作就行,还是灰常简单的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值