HDU 4417 Super Mario ( 超级马里奥 + 主席树 + 线段树/树状数组离线处理 + 划分树 )

HDU 4417 - Super Mario ( 主席树 + 线段树/树状数组离线处理 + 划分树 )

这道题有很多种做法,我先学习的是主席树。后面陆续补上线段树离线和划分树

题目大意就是给定一个区间给定一个数列,每次要求你查询区间[L,R]内不超过K的数的数量

主席树做法:

最基本的是静态第k大,这里是求静态的 <= K,差不多,在Query操作里面需要修改修改

先建立size棵主席树,然后询问的时候统计的是

第R棵主席树中[1,K]的数量 - 第L-1棵主席树中[1,K]的数量

注意这里下标从0开始,所以是L - R+1

这里有一个疑惑就是为什么用把HH[i]的值也进行建树,不然会RE,怎么回事还不知道

建树的时候记得对K序列进行离散化

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#define CLR( a, b )        memset( a, b, sizeof(a) )
using namespace std;

#define lson l, m, L[o]
#define rson m + 1, r, R[o]
#define MAXN 100005

int L[MAXN << 5], R[MAXN << 5], cnt[MAXN << 5];
int root[MAXN];
int num[MAXN], H[MAXN], tot;
int LL[MAXN], RR[MAXN], HH[MAXN];

void PushUp( int o ){
    cnt[o] = cnt[ L[o] ] + cnt[ R[o] ];
} 

void Build( int l, int r ,int &o ){
    o = tot++;
    cnt[o] = 0;
    if( l == r )
        return;
    int m = ( l + r ) >> 1;
    Build( lson );
    Build( rson );
}

void Update( int fa, int p, int l, int r ,int &o ){
    o = tot++;
    //printf("o = %d\n tot = %d\n",o,tot);
    L[o] = L[fa];
    R[o] = R[fa];
    cnt[o] = cnt[fa] + 1;
    if( l == r )
        return;

    int m = ( l + r ) >> 1;
    if( p <= m )    Update( L[fa], p, lson );
    else     Update( R[fa], p, rson );
    //PushUp( o );
}

int Query( int u, int v, int l, int r, int ll,int rr ){
    if( ll <= l && r <= rr ){
        return cnt[v] - cnt[u];
    }
    int m = ( l + r ) >> 1, ans = 0;
    if( ll <= m )        ans += Query( L[u], L[v], l, m, ll, rr );
    if( rr >  m )         ans += Query( R[u], R[v], m + 1, r, ll, rr );
    return ans;
}

void Orz(){
    int n, i, h, ans, m, cas, t;
    scanf( "%d", &t );
    for( cas = 1; cas <= t; ++cas ){
        scanf( "%d %d", &n,&m );
        int size = 0;
        for( i = 1; i <= n; ++i ){
            scanf( "%d", &num[i] );
            H[++size] = num[i];
        }
        for( i = 1; i <= m; ++i){
            scanf( "%d %d %d", &LL[i], &RR[i], &HH[i] );
            H[++size] = HH[i];
        }
        sort( H + 1, H + size + 1 );
        size = unique( H + 1, H + size + 1 ) - H - 1;
        tot = 0;
        Build( 1, size, root[0] );
        for( i = 1; i <= n; ++i ){
            num[i] = lower_bound( H + 1, H + size + 1, num[i] ) - H;
        }
        for( i = 1; i <= n; ++i ){
            Update( root[i-1], num[i], 1, size , root[i] );
        }
        printf( "Case %d:\n", cas );
        for( i = 1; i <= m; ++i ){
            h = lower_bound( H + 1, H + size + 1, HH[i] ) - H;
            ans = Query( root[LL[i]], root[RR[i]+1], 1, size, 1, h );
            printf( "%d\n", ans );
        }
    }
}

int main()
{
    Orz();
    return 0;
}

主席树解法

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#define CLR( a, b )        memset( a, b, sizeof(a) )
using namespace std;

#define lson l, m, L[o]
#define rson m + 1, r, R[o]
#define MAXN 100005

int L[MAXN << 5], R[MAXN << 5], cnt[MAXN << 5];
int root[MAXN];
int num[MAXN], H[MAXN], tot;

void PushUp( int o ){
    cnt[o] = cnt[ L[o] ] + cnt[ R[o] ];
} 

void Build( int l, int r ,int &o ){
    o = tot++;
    cnt[o] = 0;
    if( l == r )
        return;
    int m = ( l + r ) >> 1;
    Build( lson );
    Build( rson );
}

void Update( int fa, int p, int l, int r ,int &o ){
    o = tot++;
    L[o] = L[fa];
    R[o] = R[fa];
    cnt[o] = cnt[fa] + 1;
    if( l == r )
        return;

    int m = ( l + r ) >> 1;
    if( p <= m )    Update( L[fa], p, lson );
    else     Update( R[fa], p, rson );
    //PushUp( o );
}

int Query( int u, int v, int l, int r, int x ){
    if( l == r )    return ( x < l ) ? 0 : cnt[v] - cnt[u];
    //我们要求区间[L, R]中比h小的数,
    //那么就用 cnt[R] - cnt[L-1]
    int m = ( l + r ) >> 1;
    if( x <= m )    return Query( L[u], L[v], l, m, x );
    else            return cnt[L[v]] - cnt[L[u]] + Query( R[u], R[v], m + 1, r, x );
}

void Orz(){
    int n, i, j, h, ans , m, l, r, cas, t;
    scanf( "%d", &t );
    for( cas = 1; cas <= t; ++cas ){
        scanf( "%d %d", &n,&m );
        for( i = 1; i <= n; ++i ){
            scanf( "%d", &num[i] );
            H[i] = num[i];
        }
        sort( H + 1, H + n + 1 );
        int size = unique( H + 1, H + n + 1 ) - H - 1;
        for( i = 1; i <= n; ++i ){
            num[i] = lower_bound( H + 1, H + size + 1, num[i] ) - H;
        }
        tot = 0;
        Build( 1, size, root[0] );
        for( i = 1; i <= n; ++i ){
            Update( root[i-1], num[i], 1, size , root[i] );
        }
        printf( "Case %d:\n", cas);
        while( m-- ){
            scanf( "%d %d %d", &l, &r, &h );
            h = upper_bound( H + 1, H + size + 1, h ) - H - 1;
            ans = Query( root[l], root[r+1], 1, size, h );
            printf( "%d\n", ans );
        }
    }
}

int main()
{
    Orz();
    return 0;
}

按照这个思路倒是很正确

时间: 2024-10-18 22:57:08

HDU 4417 Super Mario ( 超级马里奥 + 主席树 + 线段树/树状数组离线处理 + 划分树 )的相关文章

HDU 4417 Super Mario(主席树求区间内的区间查询+离散化)

Super Mario Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 5101    Accepted Submission(s): 2339 Problem Description Mario is world-famous plumber. His “burly” figure and amazing jumping abilit

hdu 4417 Super Mario(主席树)

题意:给你一些数,有多次询问,问你在l,r区间内小于k的数有多少个 思路:主席树大发好,虽然树状数组和线段树离线也可以做 代码: #include <set> #include <map> #include <queue> #include <stack> #include <math.h> #include <vector> #include <string> #include <stdio.h> #incl

HDU 4417 Super Mario (树状数组/线段树)

Super Mario Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Description Mario is world-famous plumber. His “burly” figure and amazing jumping ability reminded in our memory. Now the poor princess is in trouble agai

hdu 4417 Super Mario(离线树状数组|划分树)

Super Mario Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 2584    Accepted Submission(s): 1252 Problem Description Mario is world-famous plumber. His "burly" figure and amazing jumping a

HDU 4417 Super Mario (划分树)(二分)

Super Mario Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 6077    Accepted Submission(s): 2645 Problem Description Mario is world-famous plumber. His "burly" figure and amazing jumping ab

HDU 4417 Super Mario(离线线段树or树状数组)

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 l

HDU 4417 Super Mario(划分树问题求不大于k的数有多少)

Super Mario Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 3625    Accepted Submission(s): 1660 Problem Description Mario is world-famous plumber. His “burly” figure and amazing jumping ability

hdu 4417 Super Mario (线段树+动态数组)

Super Mario Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 2685    Accepted Submission(s): 1306 Problem Description Mario is world-famous plumber. His "burly" figure and amazing jumping a

【划分树+二分】HDU 4417 Super Mario

第一次 耍划分树.. . 模板是找第k小的 #include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> #include <string> #include <iostream> #include <algorithm> using namespace std; #include <queue> #include &l