poj_2104: K-th Number 【主席树】

题目链接

学习了一下主席树,感觉具体算法思路不大好讲。。

大概是先建个空线段树,然后类似于递推,每一个都在前一个“历史版本”的基础上建立一个新的“历史版本”,每个历史版本只需占用树高个空间(好神奇!)

查询时这道题是通过“历史版本”间作差解决

*另外提一下,在建立“历史版本”的过程中,是“新建”,而不是“更新”,是先copy过来原有的,再按个人需求改成自己的,所产生的一个新的“历史版本”

希望注释能有帮助

//吐槽一下,本人看kuangbin模板看得好费劲,一方面不习惯用lson[],rson[]数组搞线段树,另一方面他的update query函数都是用二分写的,看半天没理解。。。

以下是按个人习惯加了注释的代码

#include<cstdio>
#include<queue>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<cmath>
#define M 100000+5
using namespace std;  

const int N=1e5+7;
const int INF=0x3f3f3f3f;

struct node
{
    int l,r;    //l,r分别指向左右孩子,但由于历史的错乱,二者的值未必有规律
    int size;    //当前历史版本里,该节点中所包含的元素个数
                //p.s. 在最后一个历史版本里,size即为总元素个数n
}tr[N*20];
//主席树的初始状态为一“空架子”
//每次updata都在前一“历史版本”基础上新建h个结点,共updata n次,产生了n个历史版本,每个版本占用空间为h*sizeof(one node)
int tot;    //结点建立顺序|所赋标号 

int n;        //共n个数
int a[N];    //读入数组
int root[N];//记录a[]中每一个数对应各“历史版本”在主席树中的根结点标号
int m;        //t[]中元素个数
int t[N];    //离散数组
int pos(int x)
{
    return lower_bound(t+1,t+1+m,x)-t;
}
int q;        //q个询问

int build(int l,int r)
{
    int rt=tot++;
    tr[rt].size=0;
    if(l==r) return rt;
    int mid=(l+r)>>1;
    tr[rt].l=build(l,mid);
    tr[rt].r=build(mid+1,r);
    return rt;
}

//    pre_node:最近一历史,[l,r]当前所更新到的包含x的区间
//    x此次更新的“终点”
//    沿途要增加的“权值”
//    p.s.  1<=l<=r<=m
int updata(int pre_node,int l,int r,int x,int v)
{
    int rt=tot++;            //rt为新结点的标号
    tr[rt]=tr[pre_node];    //先拷贝原指向当前区间的结点
    tr[rt].size+=v;
    //    在函数不断向下深入过程中,更新node.size
    if(l==r) return rt;
    int mid=(l+r)>>1;
    if(x<=mid)    tr[rt].l=updata(tr[pre_node].l,l,mid,x,v);
    else    tr[rt].r=updata(tr[pre_node].r,mid+1,r,x,v);
    return rt;
    //    在函数逐步return过程中,更新node.l|r
}

//    ql,qr反应的是在a[]中的下标,对应着更新到该下标时的“历史版本”
//    l,r反应的是在t[]中的下标
int query(int ql,int qr,int l,int r,int k)
{
    if(l==r) return l;
    int diff=tr[tr[qr].l].size-tr[tr[ql].l].size;//在更新到ql位置和更新到qr位置时两个“历史版本”下左孩子的size之差
                                                // p.s. size的大小反应较小数的多少
    int mid=(l+r)>>1;
    if(diff>=k) return query(tr[ql].l,tr[qr].l,l,mid,k);
    else return  query(tr[ql].r,tr[qr].r,mid+1,r,k-diff);
}
int main()
{
    while(~scanf("%d%d",&n,&q))
    {
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]),t[i]=a[i];
        sort(t+1,t+1+n);
        m=unique(t+1,t+1+n)-t-1;
        m++,t[m]=INF;
        tot=0;
        root[0]=build(1,m);    //初始化一个空线段树
        for(int i=1;i<=n;i++)
        {
            int order=pos(a[i]);
            root[i]=updata(root[i-1],1,m,order,1);
            //每次在上一次已完成的基础上新增h个结点
            //从根 逐步新建结点到 a[i]在线段树上所对应的位置
            //在这个逐步新建结点的过程中,要新建的结点的l,r始终满足l<=m<=r
            //这些结点每一个的size都在pre_node的基础上+1
        }
        while(q--)
        {
            int l,r,k;
            scanf("%d%d%d",&l,&r,&k);
            printf("%d\n",t[query(root[l-1],root[r],1,m,k)]);
         }
    }
}
时间: 2024-12-17 16:13:14

poj_2104: K-th Number 【主席树】的相关文章

AcWing 255. 第K小数 (主席树写法)

区间k小数是主席树的模板题目,如果区间不包含,用莫队+权值线段树也能解 主席树是可持久化线段树,所为可持久化,就是每次只新增不一样的节点,而保留前面的版本,这样可以做到查询. 如果询问时1-r,那么直接主席树,询问的是l-r,就用到前缀和思想,具体看代码注释 #include<iostream> #include<algorithm> #include<cstdio> #include<cmath> #include<vector> #inclu

POJ2104 K-th Number[主席树]

K-th Number Time Limit: 20000MS   Memory Limit: 65536K Total Submissions: 51440   Accepted: 17594 Case Time Limit: 2000MS Description You are working for Macrohard company in data structures department. After failing your previous task about key inse

zoj2112 主席树动态第k大 (主席树&amp;&amp;树状数组)

Dynamic Rankings Time Limit: 10 Seconds      Memory Limit: 32768 KB The Company Dynamic Rankings has developed a new kind of computer that is no longer satisfied with the query like to simply find the k-th smallest number of the given N numbers. They

hdoj 2665 Kth number主席树裸

Kth number Time Limit: 15000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 9417    Accepted Submission(s): 2938 Problem Description Give you a sequence and ask you the kth big number of a inteval. Input The fi

poj2104 K-th Number 主席树入门;

题目链接:K-th Number 题解:我们先把数组离散离散化一下,然后先不考虑L,R的区间的关系,我们有一个棵线段树sum[]保存的是第几大到第几大出现的个数,这样我们想要询问这颗线段数的第k大是多少可以在log(n)次下就找到,但是区间的不同,一颗线段树是解决不了的,那我们如何得到L,R区间的sum数组呢?.我们可以建N棵线段树,第一棵树是空树,然后第一个数过来我们再建一课线段树在原来树的基础上,加上这个数对sum数组的贡献,这样从第一个到第N个建N棵线段树建好,我们可以发现sum[]有前缀

4504: K个串 主席树+优先队列

这道题因为有一个数在序列中出现多次只算一次的限制.我们可以这样搞.假设在当前题意下求给定右端点的区间最值.那么我们可以预处理出每个数前一次出现的位置pre[i] .接下来从左到右加入每一个值,就是在 pre[i] + 1 —— i 这个区间内加上 v[i] 的值,这样就可以得到以当前 i 点为右端点的各个区间的值(很明显维护一下最值就好了).接下来很明显有n个版本的线段树(如果你说一开始那个空的线段树也算一个版本的话,就有n+1个),那就要用主席树动态开点.而取第K大值的操作有点像超级钢琴,不过

【POJ 2104】 K-th Number 主席树模板题

达神主席树讲解传送门:http://blog.csdn.net/dad3zz/article/details/50638026 2016-02-23:真的是模板题诶,主席树模板水过.今天新校网不好,没有评测,但我立下flag这个代码一定能A.我的同学在自习课上考语文,然而机房党都跑到机房来避难了\(^o^)/~ #include<cstdio> #include<cstring> #include<algorithm> #define for1(i,a,n) for(i

HDU 2665 Kth number 主席树裸题

题目链接 主席树详解 每次插入logn个点 这样就不需要重新建树了. #pragma comment(linker, "/STACK:1024000000,1024000000") #include <iostream> #include <fstream> #include <string> #include <time.h> #include <vector> #include <map> #include &

HDU 2665 Kth number (主席树)

题意:给定一个序列,求给定区间的第 k 小的值. 析:就是一个主席树的裸板. 代码如下: #pragma comment(linker, "/STACK:1024000000,1024000000") #include <cstdio> #include <string> #include <cstdlib> #include <cmath> #include <iostream> #include <cstring&g

[POJ2761]Feed the dogs(静态区间第k小,主席树)

题目链接:http://poj.org/problem?id=2761 题意:如题 主席树只能用模版,好菜. 1 /* 2 ━━━━━┒ギリギリ♂ eye! 3 ┓┏┓┏┓┃キリキリ♂ mind! 4 ┛┗┛┗┛┃\○/ 5 ┓┏┓┏┓┃ / 6 ┛┗┛┗┛┃ノ) 7 ┓┏┓┏┓┃ 8 ┛┗┛┗┛┃ 9 ┓┏┓┏┓┃ 10 ┛┗┛┗┛┃ 11 ┓┏┓┏┓┃ 12 ┛┗┛┗┛┃ 13 ┓┏┓┏┓┃ 14 ┃┃┃┃┃┃ 15 ┻┻┻┻┻┻ 16 */ 17 #include <algorithm>