POJ 2104 (主席树)

来源:互联网 发布:疯狂java讲义最新版pdf 编辑:程序博客网 时间:2024/05/20 14:18

问你区间第k小的数是多少,主席树入门题。

本来昨天继续看字符串后缀数组的,看完线段树想想那就顺便看看主席树把,先是找可读性高的材料,找了半天没找到几个,有图解的也是寥寥无几,看了半天感觉太抽象了,本来想放弃了,睡完午觉还是想坚持读材料,最后终于看懂原理,又要找可读性高的代码,一来二去就花了一天,吐血!,然后今天去看奶奶,回来才把这题静态第K小A掉,按照自己理解写的,其实可以不用build,但为了完整性还是习惯建一下,反正时间够。至于动态套树状数组的只能下次再看了,还是得先搞完字符串的,要加快进度了。。

#include <cstdio>#include <cstring>#include <algorithm>#include <vector>using namespace std;const int maxn=1e5+10;vector<int>v;int a[maxn],root[maxn],cnt=1;struct node{    int l,r,sum;}tr[maxn*40];int getid(int x){    return lower_bound(v.begin(),v.end(),x)-v.begin()+1;}int build(int l,int r){//这里的l,r,是左孩子和右孩子的编号。    int id=cnt++;    tr[id].l,tr[id].r,tr[id].sum=0;    if(l==r) return id;    int mid= l+r>>1;    tr[id].l =build(l,mid);    tr[id].r =build(mid+1,r);    return id;}int updata (int l,int r,int ls,int k){//每次更新只有头到叶子结点这一条链改变。    int id=cnt++;    tr[id]= tr[ls]; //先让新节点等于历史版本节点。    tr[id].sum++;    //改sum;    int mid = l+r>>1;    if (l == r) return id;    if (k<=mid) tr[id].l = updata(l,mid,tr[ls].l,k);    else tr[id].r = updata(mid+1,r,tr[ls].r,k);    return id;}int query(int l,int r,int x,int y,int k){//[1,r]-[1,l-1]=[l,r]; 找的是两个线段树做差的那棵线段树    if(l==r) return l;    int mid=l+r>>1;    int sum=tr[tr[y].l].sum-tr[tr[x].l].sum; //算左边的数,左节点的最大排名    if(k<=sum) return query(l,mid,tr[x].l,tr[y].l,k); //小于sum,在左边.    else return query(mid+1,r,tr[x].r,tr[y].r,k-sum);//在右边,继续查的时候就减掉对应排名。}int main(){    int n,m;    scanf("%d%d",&n,&m);    for(int i=1;i<=n;i++)    scanf("%d",&a[i]),v.push_back(a[i]);    sort(v.begin(),v.end()); //排序    v.erase(unique(v.begin(),v.end()),v.end()); //去重    root[0]=build(1,n);    for(int i=1;i<=n;i++)        root[i]=updata(1,n,root[i-1],getid(a[i])); //getid取离散化后排名    for(int i=1;i<=m;i++)    {        int l,r,k;        scanf("%d%d%d",&l,&r,&k);        printf("%d\n",v[query(1,n,root[l-1],root[r],k)-1]);//减1因为离散化后下标从1开始,v中存储从0开始    }}

原创粉丝点击