codeforces 487B B. Strip(rmq+线段树+二分)

题目链接:

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;
    }
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-15 04:07:45

codeforces 487B B. Strip(rmq+线段树+二分)的相关文章

Codeforces 487B. Strip DP+线段树+二分

dp[ i ]表示到第i个位置最少要分多少下, dp[ i ] = min ( dp [ i ] , dp [ j ] + 1 ) j 在合适的范围内 (  满足长度和最值差 ) 对整个数组建立线段树维护最大值和最小值这样就可在nlogn的时间里求出某一段的最值差,这个范围是满足单调性的,所以对于每个i可以二分出j的最小值 . 对每个dp[i]建立线段树,可以在nlogn时间内求出最小的j. 所以总时间复杂度n^2logn B. Strip time limit per test 1 secon

Educational Codeforces Round 64 (Rated for Div. 2) (线段树二分)

题目:http://codeforces.com/contest/1156/problem/E 题意:给你1-n  n个数,然后求有多少个区间[l,r] 满足    a[l]+a[r]=max([l,r]) 思路:首先我们去枚举区间肯定不现实,我们只能取把能用的区间去用,我们可以想下每个数当最大值的时候所做的贡献 我们既然要保证这个数为区间里的最大值,我们就要从两边扩展,找到左右边界能扩展在哪里,这里你直接去枚举肯定不行 这里我们使用了线段树二分去枚举左右区间最远能走到哪里,然后很暴力的去枚举短

hdu 4893 (多校1007)Wow! Such Sequence!(线段树&amp;二分&amp;思维)

Wow! Such Sequence! Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 352    Accepted Submission(s): 104 Problem Description Recently, Doge got a funny birthday present from his new friend, Prot

POJ3264 Balanced Lineup RMQ 线段树

求区间内最大数和最小数的差,用两棵线段树,一个维护区间最大值,一个维护区间最小值. #include <stdio.h> #include <vector> #include <math.h> #include <string.h> #include <string> #include <iostream> #include <queue> #include <list> #include <algori

Codeforces 444C DZY Loves Colors(线段树)

题目大意:Codeforces 444C DZY Loves Colors 题目大意:两种操作,1是修改区间上l到r上面德值为x,2是询问l到r区间总的修改值. 解题思路:线段树模板题. #include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> using namespace std; const int maxn = 5*1e5; typedef long lo

hdu 5592 ZYB&#39;s Premutation (线段树+二分查找)

链接: http://acm.hdu.edu.cn/showproblem.php?pid=5592 Problem Description ZYB has a premutation P,but he only remeber the reverse log of each prefix of the premutation,now he ask you to  restore the premutation.Pair (i,j)(i<j) is considered as a reverse

Codeforces 57B Martian Architecture 暴力||线段树

题目链接:点击打开链接 题意:n长的序列(初始全为0) m个操作 k个查询 下面m个操作[l,r] h 代表 a[l] +=h; a[l+1] += h+i; a[l+i] += h+i;  l<=i<=r 然后问k个位置的和 因为k<=100 所以直接暴力也可以 ----------------------- 如果k<=100000 也是可以做的 只需要给区间记录一个标记lazy,表示从左端点开始 l, l+1, l+i ··· l+r 而向下更新时, 左区间则直接更新, 右区间

Codeforces 484E Sign on Fence(可持久化线段树+二分)

题目链接:Codeforces 484E Sign on Fence 题目大意:给定给一个序列,每个位置有一个值,表示高度,现在有若干查询,每次查询l,r,w,表示在区间l,r中, 连续最长长度大于w的最大高度为多少. 解题思路:可持久化线段树维护区间合并,前端时间碰到一题可持久化字典树,就去查了一下相关论文,大概知道了是 什么东西. 将高度按照从大到小的顺序排序,然后每次插入一个位置,线段树维护最长连续区间,因为插入是按照从大到小的顺 序,所以每次的线段树中的连续最大长度都是满足高度大于等于当

codeforces 589G:线段树+二分

离线做 先按照t[]建线段树,维护区间的有效天数以及可用时间 然后把t[]从小到达排序,记得记录t中元素在线段树中的位置 把询问按照d从小到大排序,依次考虑 由于按照d排序,所以当前询问肯定是d最短的,我们在t数组中找到大于当前d的第一个元素位置,把这之前的元素全部从线段树中删除,表示这些天数无效 因为当前d已经是最小,所以删除对后续不会有影响 二分一个天数,查找0~mid天一共的工作时间(工作时间=总可用时间-准备时间*有效天数) #include"cstdio" #include&