POJ2104 K-th Number(主席树)

题目大意:查询区间k大值

 

关于主席树:

主席树(可持久化线段树,函数式线段树)

解决区间k大值 用到前缀和思想
如果前L-1的3个最大值1 2 3 前R个的3个最大值是4 5 6
那么前三大就是4 5 6
那如果前R个数前三大是1 5 6呢?那就必须要知道没个数的个数了!因为后面的数2与前面的数做差不就可以了吗?
但题目是k个数啊 那就维护k个数呗! 线段树!
建立n+1棵线段树,统计每个数到sz[i]时出现的次数,线段树R比线段树L-1多出来的数中第k大就是答案!但超空间啊
把所有数排序,去重,线段树中存储的不是数本身而是数的排序 还是空间......
解决方法:公用节点。

建立过程:
1.排序,去重

sz 2 4 6 8 8 6 4 2
hash 2 4 6 8
改成序号 1 2 3 4 4 3 2 1
2.建立线段树
先建立空线段树(要用到前缀和)

然后再根据每一个hash值建立线段树,每加一个数就增加一条链

实现跟线段树差不多

//主席树模板 
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
const int maxn=100005;
const int maxnn=10000000;
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]=0;
    if(l!=r)
    {
        int mid=(l+r)/2;
        build(ls[cur],l,mid);
        build(rs[cur],mid+1,r);
    }
}

void updata(int pre,int ps,int &cur,int l,int r)//往上加线段树 
{
    cur=tot++;
    cnt[cur]=cnt[pre]+1;
    ls[cur]=ls[pre];rs[cur]=rs[pre];
    if(l==r)return;
    int mid=(l+r)/2;
    if(ps<=mid)updata(ls[pre],ps,ls[cur],l,mid);
    else updata(rs[pre],ps,rs[cur],mid+1,r);
}

int query(int lt,int rt,int l,int r,int k)//查询k大值 
{
    if(l==r) return l;
    int mid=(l+r)/2,differ=cnt[ls[rt]]-cnt[ls[lt]];
    if(k<=differ)return query(ls[lt],ls[rt],l,mid,k);
    else return query(rs[lt],rs[rt],mid+1,r,k-differ); 
}

int main()
{
    int n,m,l,r,k;
    while(scanf("%d%d",&n,&m)==2)
    {
        for(int i=1;i<=n;i++)
        {
            scanf("%d",sz+i);
            hash[i]=sz[i];
        }
        sort(hash+1,hash+n+1);
        int size=unique(hash+1,hash+n+1)-hash-1;//数组去重 
        for(int i=1;i<=n;i++)
          sz[i]=lower_bound(hash+1,hash+size+1,sz[i])-hash;//得到序号 
        tot=0;
        build(root[0],1,size);
        for(int i=1;i<=n;++i)
               updata(root[i-1],sz[i],root[i],1,size);
        while(m--)    
        {
            scanf("%d%d%d",&l,&r,&k);
            printf("%d\n",hash[query(root[l-1],root[r],1,size,k)]);
        }
    }
    return 0;
}
心若向阳,无谓悲伤

 

//hja 大神的代码 指针版 
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

#define nd(x) (nil + (x))

using namespace std;

const int lim = 1e9; 
const int N = 100010; 
const int lg = 60; 

struct node {
    int s; 
    node *lc, *rc; 
};
node nil[N * lg], *rot[N]; 
int cnt; 

int n, m, a[N], dc[N]; 

node* insert(node *u, int l, int r, int p) {
    node *ret = nd(++cnt);
    *ret = *u, ret->s ++;
    if(l == r) return ret;
    
    int mid = (l + r) / 2; 
    if(p <= mid) ret->lc = insert(ret -> lc, l, mid, p); 
    else ret->rc = insert(ret -> rc, mid + 1, r, p); 
    return ret;
}

int query(node *u, node *v, int l, int r, int k) {
    if(l == r) return dc[l]; 
    int mid = (l + r) / 2; 
    int c = u -> lc -> s - v -> lc -> s; 
    if(k <= c) return query(u -> lc, v -> lc, l, mid, k);
    return query(u -> rc, v -> rc, mid + 1, r, k - c); 
}

void read() {
    cin >> n >> m; 
    for(int i = 1; i <= n; i++) cin >> a[i]; 
}

void prep() {
    for(int i=1; i<=n; i++) dc[i] = a[i];
    sort(dc+1, dc+n+1), dc[0] = unique(dc+1, dc+n+1) - dc - 1;
    for(int i=1; i<=n; i++) a[i] = lower_bound(dc+1, dc+dc[0]+1, a[i]) - dc;
}

void solve() {
    cnt = nil->s = 0, rot[0] = nil->lc = nil->rc = nil;
    for(int i = 1; i <= n; i++) rot[i] = insert(rot[i-1], 1, dc[0], a[i]);
    
    while(m--) {
        int l, r, k;
        cin >> l >> r >> k; 
        cout << query(rot[r], rot[l - 1], 1, dc[0], k) << endl; 
    }
}

int main() {
    read();
    prep();
    solve(); 
    return 0; 
}
世外桃源

 

posted @ 2017-01-16 11:23  安月冷  阅读(146)  评论(0编辑  收藏  举报