HNOI2017 影魔

题解

Prei 表示 i 前面恰好比i大的位置, Sufi 表示 i 后面恰好比i大的位置。
对于一个询问 [l,r] 考虑将 p1,p2 的贡献分开计算。对于 p1 ,要满足 max(ki+1,...,kj1)min(ki,kj) ,一个位置 i 将其当做较小值,那么贡献就是ri=l[Preil]+[Sufir],所以可以用主席树维护。对于 p2 ,要满足 min(ki,kj)max(ki+1,...,kj1)max(ki,kj) 不太好处理,考虑只求满足 max(ki+1,...,kj1)max(ki,kj) ,然后减去 p1 的贡献,一个位置 i 将其当做较大值,那么贡献就是ri=limax(Prei+1,l)+min(Sufi1,r)也可以讨论一下拆开 min,max 然后主席树维护。

时间复杂度: O(nlogn)

SRC

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std ;

#define N 200000 + 10
const int MAXN = 19 ;
typedef long long ll ;
struct Query {
    int l , r ;
} Q[N] ;
struct Tree {
    int Son[2] ;
    int tot ;
    ll Sum1 , Sum2 ;
} T[20*N] ;

int Tab[N] ;
int f[N][MAXN] ;
int a[N] , Pre[N] , Next[N] , Root[N] ;
int n , m , p1 , p2 , Cnt ;
ll tot1[N] , tot2[N] ;
ll ret1 , ret2 , ret3 ;

int Read() {
    int ret = 0 ;
    char ch = getchar() ;
    while ( ch < '0' || ch > '9' ) ch = getchar() ;
    while ( ch >= '0' && ch <= '9' ) {
        ret = ret * 10 + ch - '0' ;
        ch = getchar() ;
    }
    return ret ;
}

int Find( int l , int r ) {
    int k = Tab[r-l+1] ;
    return max( f[l][k] , f[r-(1<<k)+1][k] ) ;
}

int Lbinary( int l , int r , int s ) {
    int ret = s ;
    while ( l <= r ) {
        int mid = (l + r) >> 1 ;
        if ( Find( mid , s ) <= a[s]  ) ret = mid , r = mid - 1 ;
        else l = mid + 1 ;
    }
    return ret ;
}

int Rbinary( int l , int r , int s ) {
    int ret = s ;
    while ( l <= r ) {
        int mid = (l + r) >> 1 ;
        if ( Find( s , mid ) <= a[s] ) ret = mid , l = mid + 1 ;
        else r = mid - 1 ;
    }
    return ret ;
}

int NewNode( int last ) {
    ++ Cnt ;
    T[Cnt] = T[last] ;
    return Cnt ;
}

void Insert( int v , int l , int r , int x , int val ) {
    if ( l == r ) {
        T[v].tot ++ ;
        T[v].Sum1 += x ;
        T[v].Sum2 += val ;
        return ;
    }
    int mid = (l + r) >> 1 ;
    if ( x <= mid ) {
        T[v].Son[0] = NewNode( T[v].Son[0] ) ;
        Insert( T[v].Son[0] , l , mid , x , val ) ;
    } else {
        T[v].Son[1] = NewNode( T[v].Son[1] ) ;
        Insert( T[v].Son[1] , mid + 1 , r , x , val ) ;
    }
    T[v].tot = T[T[v].Son[0]].tot + T[T[v].Son[1]].tot ; 
    T[v].Sum1 = T[T[v].Son[0]].Sum1 + T[T[v].Son[1]].Sum1 ;
    T[v].Sum2 = T[T[v].Son[0]].Sum2 + T[T[v].Son[1]].Sum2 ;
}

void Search( int lv , int rv , int l , int r , int x , int y ) {
    if ( T[rv].tot == T[lv].tot ) return ;
    if ( l == x && r == y ) {
        ret1 += T[rv].Sum1 - T[lv].Sum1 ;
        ret2 += T[rv].Sum2 - T[lv].Sum2 ;
        ret3 += T[rv].tot - T[lv].tot ;
        return ;
    }
    int mid = (l + r) >> 1 ;
    if ( y <= mid ) Search( T[lv].Son[0] , T[rv].Son[0] , l , mid , x , y ) ;
    else if ( x > mid ) Search( T[lv].Son[1] , T[rv].Son[1] , mid + 1 , r , x , y ) ;
    else {
        Search( T[lv].Son[0] , T[rv].Son[0] , l , mid , x , mid ) ;
        Search( T[lv].Son[1] , T[rv].Son[1] , mid + 1 , r , mid + 1 , y ) ;
    }
}

int main() {
    freopen( "sf.in" , "r" , stdin ) ;
    freopen( "sf.out" , "w" , stdout ) ;
    n = Read() , m = Read() , p1 = Read() , p2 = Read() ;
    Tab[1] = 0 ;
    for (int i = 1 ; i <= n ; i ++ ) {
        a[i] = Read() ;
        f[i][0] = a[i] ;
        if ( i > 1 ) Tab[i] = Tab[i/2] + 1 ;
    }
    for (int j = 1 ; j < MAXN ; j ++ ) {
        for (int i = 1 ; i + (1 << (j - 1)) <= n ; i ++ ) {
            f[i][j] = max( f[i][j-1] , f[i+(1<<(j-1))][j-1] ) ;
        }
    }
    for (int i = 1 ; i <= n ; i ++ ) {
        Pre[i] = Lbinary( 1 , i , i ) - 1 ;
        Next[i] = Rbinary( i , n , i ) + 1 ;
    }

    for (int i = 1 ; i <= n ; i ++ ) {
        Root[i] = NewNode( Root[i-1] ) ;
        Insert( Root[i] , 0 , n + 1 , Pre[i] , i ) ;
    }
    for (int i = 1 ; i <= m ; i ++ ) {
        Q[i].l = Read() , Q[i].r = Read() ;
        int l = Q[i].l , r = Q[i].r ;
        ret1 = ret2 = ret3 = 0 ;
        Search( Root[Q[i].l-1] , Root[Q[i].r] , 0 , n + 1 , Q[i].l , n + 1 ) ;
        tot1[i] = ret3 ;
        tot2[i] = ret2 - ret1 - ret3 ;
        ll S = (l + r) * (r - l + 1) / 2 ;
        tot2[i] += (S - ret2) - l * ((r - l + 1) - ret3) ;
    }

    Cnt = 0 ;
    memset( T , 0 , sizeof(T) ) ;

    for (int i = 1 ; i <= n ; i ++ ) {
        Root[i] = NewNode( Root[i-1] ) ;
        Insert( Root[i] , 0 , n + 1 , Next[i] , i ) ;
    }
    for (int i = 1 ; i <= m ; i ++ ) {
        int l = Q[i].l , r = Q[i].r ;
        ret1 = ret2 = ret3 = 0 ;
        Search( Root[Q[i].l-1] , Root[Q[i].r] , 0 , n + 1 , 0 , Q[i].r ) ;
        tot1[i] += ret3 ;
        tot2[i] += ret1 - ret2 - ret3 ;
        ll S = (l + r) * (r - l + 1) / 2 ;
        tot2[i] += r * ((r - l + 1) - ret3) - (S - ret2) ;
    }

    for (int i = 1 ; i <= m ; i ++ ) {
        tot2[i] -= tot1[i] ;
        printf( "%lld\n" , tot1[i] * p1 + tot2[i] * p2 ) ;
    }
    return 0 ;
}

以上.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值