hdu 4417 可持久化线段树 (区间<=x的数的数量

本文介绍了一种高效的数据结构——可持久化线段树,用于解决区间查询问题。通过对线段树进行优化,使得在不改变历史版本的情况下支持多次查询及更新操作。文章详细解释了其实现原理,并通过一道具体题目展示了其应用。



链接:戳这里


Super Mario

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)

Problem Description
Mario is world-famous plumber. His “burly” figure and amazing jumping ability reminded in our memory. Now the poor princess is in trouble again and Mario needs to save his lover. We regard the road to the boss’s castle as a line (the length is n), on every integer point i there is a brick on height hi. Now the question is how many bricks in [L, R] Mario can hit if the maximal height he can jump is H.
 
Input
The first line follows an integer T, the number of test data.
For each test data:
The first line contains two integers n, m (1 <= n <=10^5, 1 <= m <= 10^5), n is the length of the road, m is the number of queries.
Next line contains n integers, the height of each brick, the range is [0, 1000000000].
Next m lines, each line contains three integers L, R,H.( 0 <= L <= R < n 0 <= H <= 1000000000.)
 
Output
For each case, output "Case X: " (X is the case number starting from 1) followed by m lines, each line contains an integer. The ith integer is the number of bricks Mario can hit for the ith query.
 
Sample Input
1
10 10
0 5 2 7 5 4 3 8 7 7 
2 8 6
3 5 0
1 3 1
1 9 4
0 1 0
3 5 5
5 5 1
4 6 3
1 5 7
5 7 3
 
Sample Output
Case 1:
4
0
0
3
1
2
0
1
5
1
 


题意:

长度为n(1e5)的整数序列,m(1e5)个询问 l r x ,每组询问输出区间[l,r]内有多少个数比<=x


思路:

第一道可持久化线段树。

个人的理解:考虑在线段树上支持一些操作,同时能够维护所有的线段树的历史版本。

一次修改操作更改的节点只有logn个,只重建这些点并且尽量重用其他的点,就是我们的目的。

递归来看待这个问题:首先考虑一个叶子节点,那么我们只需要新建一个叶子节点,它的值是修改过的值,那么就能得到一个当前的版本。

再考虑一个非叶子节点,由于它的两个孩子中最多只会有一个被修改,不妨看成左孩子,那么我们对左孩子递归下去,得到左孩子的修改版本,然后将当前节点拷贝一份,右孩子不变,左孩子为修改后的版本,就得到了当前节点修改后的版本。


一次插入的是1 2 3 4  

这道题是快速得到区间内<=x的值的个数

那么考虑到当前i为止,每个数出现的次数,离散化将所有数铺在线段树上。从i-1版本的线段树继承过来

那么每次询问的时候,答案就是第r个版本的线段树上的>=x的数的和-第l-1个版本的线段树上的>=x的数的和


代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<vector>
#include <ctime>
#include<queue>
#include<set>
#include<map>
#include<list>
#include<stack>
#include<iomanip>
#include<cmath>
#include<bitset>
#define mst(ss,b) memset((ss),(b),sizeof(ss))
///#pragma comment(linker, "/STACK:102400000,102400000")
typedef long long ll;
typedef long double ld;
#define INF (1ll<<60)-1
const int Max=30*100000;
using namespace std;
struct node{
    int sum,l,r;
}tr[Max];
int tot=0;
void build(int &root,int l,int r){
    root=++tot;
    if(l==r) {
        tr[root].sum=0;
        tr[root].l=root;
        tr[root].r=root;
        return ;
    }
    int mid=(l+r)/2;
    build(tr[root].l,l,mid);
    build(tr[root].r,mid+1,r);
    tr[root].sum=tr[tr[root].l].sum+tr[tr[root].r].sum;
}
void update(int last,int &root,int l,int r,int x,int v){
    root=++tot;
    tr[root]=tr[last];
    if(l==r) {
        tr[root].sum+=v;
        tr[root].l=root;
        tr[root].r=root;
        return ;
    }
    int mid=(l+r)/2;
    if(x<=mid) update(tr[last].l,tr[root].l,l,mid,x,v);
    else update(tr[last].r,tr[root].r,mid+1,r,x,v);
    tr[root].sum=tr[tr[root].l].sum+tr[tr[root].r].sum;
}
int query(int root,int l,int r,int x){
    if(l==r){
        return tr[root].sum;
    }
    int mid=(l+r)/2;
    if(x>mid) return tr[tr[root].l].sum+query(tr[root].r,mid+1,r,x);
    else return query(tr[root].l,l,mid,x);
}
void print(int root,int l,int r){
    printf("%d %d %d %d\n",root,tr[root].l,tr[root].r,tr[root].sum);
    if(l==r) return ;
    int mid=(l+r)/2;
    print(tr[root].l,l,mid);
    print(tr[root].r,mid+1,r);
}
int root[Max];
ll a[100100],b[100100];
int n,m;
int main(){
    int T;
    scanf("%d",&T);
    for(int cas=1;cas<=T;cas++){
        tot=0;
        mst(root,0);
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++) {
            scanf("%I64d",&a[i]);
            b[i]=a[i];
        }
        sort(b+1,b+n+1);
        int cnt=unique(b+1,b+n+1)-(b+1);
        b[++cnt]=INF;
        build(root[0],1,cnt);
        for(int i=1;i<=n;i++){
            int x=lower_bound(b+1,b+cnt+1,a[i])-b;
            update(root[i-1],root[i],1,cnt,x,1);
        }
        printf("Case %d:\n",cas);
        for(int i=1;i<=m;i++){
            int l,r;
            ll v;
            scanf("%d%d%I64d",&l,&r,&v);l++;r++;
            int x=upper_bound(b+1,b+cnt+1,v)-b-1;
            int R=0,L=0;
            if(x>0){
                L=query(root[l-1],1,cnt,x);
                R=query(root[r],1,cnt,x);
            }
            printf("%d\n",R-L);
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值