题目链接:

codeforces 487B


题目大意:

给出一个序列,要把序列划分成段,每一段最少有L个元素,段中的最大元素和最小元素之差不大于s,问划分的段的最少的数量是多少。


题目分析:

  • 首先用rmq维护区间最大值和区间最小值。
  • 然后按顺序扫描数组,线段树维护的数组,每个记录当前点作为最后一个点的前i个点划分的最小的段数,那么每次更新就是二分找到可以转移到我的最远距离,然后再选取与我距离大于l的那部分,取最小值即可。
  • 最终结果就是线段树维护的数组的最后一个位置的元素的值。

AC代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define MAX 100007

using namespace std;

int n,s,l,a[MAX];
int dp[MAX][30];
int dp2[MAX][30];

void make ( )
{
    for ( int i = 1 ; i <= n ; i++ )
        dp[i][0] = a[i],dp2[i][0] = a[i];
    for ( int j = 1 ; (1<<j) <= n ; j++ )
        for ( int i = 1 ; i + (1<<j) -1 <= n ; i++ )
        {
            dp[i][j] = max ( dp[i][j-1] , dp[i+(1<<(j-1))][j-1] );
            dp2[i][j] = min ( dp2[i][j-1] , dp2[i+(1<<(j-1))][j-1] );
        }   
}

int big ( int l , int r )
{
    int k = (int)((log(r-l+1)*1.0)/(log(2.0)));
    return max ( dp[l][k] , dp[r-(1<<k)+1][k] );
}

int small ( int l , int r )
{
    int k = (int)((log(r-l+1)*1.0)/(log(2.0)));
    return min ( dp2[l][k] , dp2[r-(1<<k)+1][k] );
}


struct Tree
{
    int minn,l,r;
}tree[MAX<<2];

void build ( int u , int l, int r )
{
    tree[u].l = l;
    tree[u].r = r;
    tree[u].minn = 1<<30;
    if ( l == r ) return;
    int mid = (l+r)>>1;
    build ( u<<1 , l , mid );
    build ( u<<1|1 , mid+1 , r );
}

void push_up ( int u )
{
    tree[u].minn = min ( tree[u<<1].minn , tree[u<<1|1].minn );
}

void update ( int u , int x , int v )
{
    int l = tree[u].l;
    int r = tree[u].r;
    if ( l == r)
    {
        tree[u].minn = v;
        return;
    }
    int mid = l+r>>1;
    if ( x > mid )
        update ( u<<1|1 , x , v );
    else 
        update ( u<<1 , x , v );
    push_up (u );
}

int query ( int u , int left , int right )
{
    int l = tree[u].l;
    int r = tree[u].r;
    if ( left <= l && r <= right )
        return tree[u].minn;
    int ret = 1<<30;
    int mid = l+r>>1;
    if ( left <= mid && right >= l )
        ret = min ( ret , query ( u<<1 , left , right ) );
    if ( left <= r && right > mid )
        ret = min ( ret , query ( u<<1|1 , left , right ) );
    return ret;
}

int bsearch ( int x )
{
    int l = 1, r = x , mid;
    while ( l < r )
    {
        mid = (l+r)>>1;
        if ( big ( mid , x ) - small( mid , x ) > s ) l = mid+1;
        else r = mid;
    }
    return l;
}

int main () 
{
    while ( ~scanf ( "%d%d%d" , &n , &s , &l ) )
    {
        for ( int i = 1 ; i <= n ; i ++ )
            scanf ( "%d" , &a[i] );
        make ( );
        build ( 1 , 1 , n );
        for ( int i = 1 ; i <= n ; i++ )
        {
            int ll = bsearch ( i )-1;
            int rr = i-l;
            if ( rr < ll ) continue;
            if ( ll <= 0 )
            {
                update ( 1 , i , 1 );
                continue;
            }
            int x = query ( 1 , ll , rr );
            update ( 1 , i , x+1 );
        }
        int x = query ( 1 , n , n );
        if ( x == 1<<30 )
            puts ("-1" );
        else 
            printf ( "%d\n" , x );
        return 0;
    }
}