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