hdu 3727(主席树例题)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3727

题意是有4种操作

1、在项链后面插入一个珍珠,保证每一个珍珠都不一样
2、查询第 l 到 第 r 个珍珠之间第k大的珍珠的大小
3、假设把所有珍珠按照大小排序,查询size为x的珍珠的排名
4、查询所有珍珠里第k大的珍珠的大小

题目只需要输出2,3,4询问的所有答案即可

第3个询问很简单,不谈。第4个询问本质上和上一道CWOJ的题是一样的。而第2个操作需要用到主席树的思想。

代码大致思路

建立n棵线段树T(i),T(i).sum记录[1, i]区间内有多少个数,线段树上是以数的数值从小到大来建立的,比如T(i)(x)表示的就是[1, i]区间内大小为 i 的数有多少个(这里的大小指的是离散化之后的)

用主席树的方法来建立这n棵线段树

查询操作用主席树的方法来查询即可

//Hello. I'm Peter.
#include<cstdio>
#include<iostream>
#include<sstream>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<algorithm>
using namespace std;
typedef long long ll;

#define N 100010
#define M 35010
int n;
char s[100];

//存储询问
struct Query{
    int ty, x;
    int l, r, k;
}q[N + 3 * M];

#define MAXN (N + M) * (4 + 17)
int lch[MAXN], rch[MAXN], val[MAXN], T[N], nnod, tem[N + M], ntem;

//建立一棵空线段树
int plant(int l, int r){
    int t = ++nnod;
    val[t] = 0;
    if(l == r) return t;
    int mid = (l + r) >> 1;
    lch[t] = plant(l, mid);
    rch[t] = plant(mid + 1, r);
    return t;
}

//建立每棵线段树
int update(int id, int p, int l, int r, int v){
    int t = ++nnod;
    lch[t] = lch[id], rch[t] = rch[id], val[t] = val[id] + v;
    if(l == r) return t;
    int mid = (l + r) >> 1;
    if(p <= mid) lch[t] = update(lch[t], p, l, mid, v);
    else rch[t] = update(rch[t], p, mid + 1, r, v);
    return t;
}

//
int query(int lid, int rid, int l, int r, int k){
    if(l == r) return l;
    int mid = (l + r) >> 1;
    int lsum = val[lch[rid]] - val[lch[lid]];//容易出错
    if(lsum >= k) return query(lch[lid], lch[rid], l, mid, k);
    else return query(rch[lid], rch[rid], mid + 1, r, k - lsum);
}

//
int query2(int id, int ql, int qr, int l, int r){
    if(ql == l && qr == r) return val[id];
    int mid = (l + r) >> 1;
    if(qr <= mid) return query2(lch[id], ql, qr, l, mid);
    else if(mid < ql) return query2(rch[id], ql, qr, mid + 1, r);
    else return query2(lch[id], ql, mid, l, mid) + query2(rch[id], mid + 1, qr, mid + 1, r);
}

int main(){
    ll ans1, ans2, ans3;
    int kase = 1;
    while(~scanf("%d",&n)){
        ntem = 0;
        for(int i = 1; i <= n; i++){
            scanf("%s",s);
            if(s[0] == 'I'){
                q[i].ty = 0;
                scanf("%d", &q[i].x);
                tem[++ntem] = q[i].x;
            }
            else if(s[6] == '1'){
                q[i].ty = 1;
                scanf("%d%d%d",&q[i].l, &q[i].r, &q[i].k);
            }
            else if(s[6] == '2'){
                q[i].ty = 2;
                scanf("%d",&q[i].x);
                tem[++ntem] = q[i].x;
            }
            else if(s[6] == '3'){
                q[i].ty = 3;
                scanf("%d",&q[i].k);
            }
        }

        //离散化
        sort(tem + 1, tem + 1 + ntem);
        ntem = (int)(unique(tem + 1, tem + 1 + ntem) - (tem + 1));
        for(int i = 1; i <= n; i++){
            if(q[i].ty == 0 || q[i].ty == 2){
                q[i].x = (int)(lower_bound(tem + 1, tem + 1 + ntem, q[i].x) - tem);
            }
        }

        nnod = 0;
        T[0] = plant(1, ntem);
        int nowp = 0;
        ans1 = ans2 = ans3 = 0;
        for(int i = 1; i <= n; i++){
            if(q[i].ty == 0){
                ++nowp;
                T[nowp] = update(T[nowp - 1], q[i].x, 1, ntem, +1);
            }
            else if(q[i].ty == 1){
                int t = query(T[q[i].l - 1], T[q[i].r], 1, ntem, q[i].k);
                ans1 += tem[t];
            }
            else if(q[i].ty == 2){
                ans2 += query2(T[nowp], 1, q[i].x, 1, ntem);
            }
            else if(q[i].ty == 3){
                int t = query(T[0], T[nowp], 1, ntem, q[i].k);
                ans3 += tem[t];
            }
        }

        printf("Case %d:\n",kase++);
        //记得hdu上用%I64d
        printf("%I64d\n%I64d\n%I64d\n",ans1,ans2,ans3);
        //printf("%lld\n%lld\n%lld\n",ans1,ans2,ans3);
    }
    return 0;
}

你可能感兴趣的:(hdu 3727(主席树例题))