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;
}
时间: 2024-11-15 21:32:23

hdu 3727(主席树例题)的相关文章

hdu 5919 主席树(区间不同数的个数 + 区间第k大)

Sequence II Time Limit: 9000/4500 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 849    Accepted Submission(s): 204 Problem Description Mr. Frog has an integer sequence of length n, which can be denoted as a1,a2,?

HDU 2665(主席树,无修改第k小)

Kth number                                                 Time Limit: 15000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)                                                                        Total Submission(s): 10682    Accep

HDU 6278 主席树(区间第k大)+二分

Just h-index Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 132768/132768 K (Java/Others)Total Submission(s): 438    Accepted Submission(s): 203 Problem Description The h-index of an author is the largest h where he has at least h papers wit

hdu 5919 主席树入门题

主席树是从右往左初始化,每次把这个数出现过的位置消去,然后在当前位置加一. 然后我的做法是查两遍,第一遍能找出不同的个数,除一半:再用这个值查,一直到底,最后返回位置,比较套路的一题. #include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> #include <vector> #include <queue> #include &l

HDU 4251 --- 主席树(划分树是正解)

题意:查询区间中位数 思路:模板题,相当于区间第K大的数,主席树可以水过,但划分树是正解.但还没搞明白划分树,先上模板 1 #include <iostream> 2 #include <cstdio> 3 #include <cmath> 4 #include <cstring> 5 #include <cstdlib> 6 #include <string> 7 #include <vector> 8 #include

hdu 5140 主席树

这题说的是每个员工有工资 水平 在公司待的年限这几个属性,有大量的查询 查的是在一定的水平和工作年限的工人总工资是多少 这个思路是比较简单的我们按照他们的水平排序,排完后,使用主席树不断地往里面插,然后查询即可 但是有一个问题就是 可能有些点不存在 因为这题不能讲所有的点全部离散 #include <iostream> #include <cstdio> #include <algorithm> #include <string.h> #include &l

主席树入门

主席树又叫可持久化权值线段树,一开始使用来解决第k大的问题,因其发明者黄嘉泰名字的首字母和某人的一样,所以被叫做主席树. 在了解主席树之前,我们先认识一下什么叫做权值线段树. 给你n个数,问你这n个数中第k小的数是哪个.像这种题我们一般都是直接排序然后暴力找,但是我们今天用线段树来试试. 例如a[12]={1,5,7,3,2,6,8,1,3,5,5,2},你要求出a数组中第7小的数的值.我们先建棵线段树用来存每个值出现的次数,按下标从小到大依次将数组中的元素放入插入线段树中. 插入前三个数后,线

hdu 4605 Magic Ball Game (在线主席树/离线树状数组)

hdu 4605 题意: 有一颗树,根节点为1,每一个节点要么有两个子节点,要么没有,每个节点都有一个权值wi .然后,有一个球,附带值x . 球到达某个节点上,如果x==wi,那么球停在这个节点上 .当然,这个点是叶子节点也会停止 . 如果x<wi,那么有1/2的概率走向左子树,有1/2的概率走向右子树 . 如果x>wi,那么有1/8的概率走向左子树,有7/8的概率走向右子树 . 问球经过v节点的概率 .(停在v节点也算) 解法: 在线的话每一个节点建一棵根节点到该节点的线段树,离线的话就先

hdu 4348 To the moon(主席树区间操作)

题目链接:hdu 4348 To the moon 题意: 给你n个数,有m个操作. 1.给区间[l,r]的所有数+d,并且时间戳+1 2.询问当前时间戳的区间和. 3.询问过去时间戳t的区间和. 4.退回到时间戳t. 题解: 直接上主席树. 不过区间操作的时候push_down空间似乎不是那么够用. 所有把push_down这个操作去掉. 用一个标记记录当前这个区间的累加. 询问的时候就将这个累加传下去.(具体看代码) 最后还有退回状态t的时候可以把cnt=root[t+1], 因为后面的内存