hdu 4417 Super Mario (主席树)

链接:http://acm.hdu.edu.cn/showproblem.php?pid=4417

题意:

给你段长为n的序列,有q个询问,每次询问区间[l.r]内有多少个数小于等于k

思路:

之前用分块写过类似的,不过为了练习下主席树,这里用主席树写了下。思路很简单

离线离散化处理下,每次插入一个数num时,在主席树上下标num+1,这样每次询问[l,r]中有多少个小于k的数的时候,我们只要找下标【1,k】的区间第R次修改后的总和减去第L-1次修改后的总值就可以得到了

 

实现代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int M = 2e5+10;
int ls[M*40],rs[M*40],sum[M*40],a[M],b[M],root[M];
int idx;
void build(int &k,int l,int r){
    k = ++idx;
    sum[k] = 0;
    if(l == r) return ;
    int mid = (l + r) >> 1;
    build(ls[k],l,mid); build(rs[k],mid+1,r);
}

void update(int old,int &k,int l,int r,int p,int c){
    k = ++idx;
    ls[k] = ls[old]; rs[k] = rs[old];
    sum[k] = sum[old] + c;
    if(l == r) return ;
    int mid = (l + r) >> 1;
    if(p <= mid) update(ls[old],ls[k],l,mid,p,c);
    else update(rs[old],rs[k],mid+1,r,p,c);
}

int query(int old,int k,int L,int R,int l,int r){
    if(L <= l&&R >= r) return sum[k] - sum[old];
    int mid = (l + r) >> 1;
    int ret = 0;
    if(L <= mid) ret += query(ls[old],ls[k],L,R,l,mid);
    if(R > mid) ret += query(rs[old],rs[k],L,R,mid+1,r);
    return ret;
}
int l[M],r[M],x[M],cnt;
int Find(int x){
    int num = lower_bound(b+1,b+1+cnt,x)-b;
    return num;
}

int main()
{
    int t,q,n,cas=1;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&q);
        for(int i = 1;i <= n;i ++)
            scanf("%d",&a[i]),b[i] = a[i];
        for(int i = 1;i <= q;i ++)
            scanf("%d%d%d",&l[i],&r[i],&x[i]),b[i+n]=x[i];
        sort(b+1,b+1+n+q);
        cnt = unique(b+1,b+1+n+q)-b-1;
        for(int i = 1;i <= n;i++){
            int fx = Find(a[i]);
            update(root[i-1],root[i],1,cnt,fx,1);
        }
        printf("Case %d:\n",cas++);
        for(int i = 1;i <= q;i ++){
            int fx = Find(x[i]);
            printf("%d\n",query(root[l[i]],root[r[i]+1],1,fx,1,cnt));
        }
    }
    return 0;
}

 

posted @ 2018-08-31 22:28  冥想选手  阅读(186)  评论(0编辑  收藏  举报