Codeforces Round #278 (Div. 2) D

D. Strip

题目链接:http://codeforces.com/contest/488/problem/D

题意:

给你一列数,让你将这列数分成连续的几段,有两个要求

1.每段的长度不能小于l

2.每段的最大值和最小值的差值不能超过s

问在满足要求下的最小划分的椴树

思路:

先用RMQ预处理出来每个每个区间的最大值和最小值

定义dp[i]表示处理0~i区间的最小段数,转移的方程是dp[i]=min(dp[i],dp[k]+1)    (1.k<=i 2.i-k+1>=l 3. k~l上的最大值和最小值<=s)

其复杂度为O(n^2),所以果断T了,所以我们就要来想一种方法来加速这个过程

如果将dp值打出来的话我们能够发现,除了没有更新的INF之外dp值是单调递增的,所以我们可以用尺取法大大简化过程,~~~没有注意其中INF的情况 wa了一个早上。

当然也可以用单调队列来优化了~~。

code:

#include <bits/stdc++.h>
#define INF 1000000000

using namespace std;

const int maxn=100500;

int a[maxn];
int flog[maxn<<1];
int dmin[maxn][30],dmax[maxn][30];
int dp[maxn];
int n,s,l;

void RMQ_init(int A[],int len)
{
    flog[0]=-1;
    for(int i=1;i<(maxn<<1);i++) flog[i]=flog[i>>1]+1;
    for(int i=0;i<len;i++) dmin[i][0]=dmax[i][0]=A[i];
    for(int j=1;(1<<j)<=len;j++){
        for(int i=0;i+(1<<j)-1<len;i++){
            dmin[i][j]=min(dmin[i][j-1],dmin[i+(1<<(j-1))][j-1]);
            dmax[i][j]=max(dmax[i][j-1],dmax[i+(1<<(j-1))][j-1]);
        }
    }
}
int RMQ(int L,int R,int kind)
{
    int k=flog[R-L+1];
    if(kind) return max(dmax[L][k],dmax[R-(1<<k)+1][k]);
    else    return min(dmin[L][k],dmin[R-(1<<k)+1][k]);
}

void debug()
{
    for(int i=0;i<n;i++){
        printf("dp[%d]=%d\n",i,dp[i]);
    }
    return ;
}

void solve()
{
    int he,ta;
    fill(dp,dp+n,INF);
    RMQ_init(a,n);
    he=0;
    ta=l-1;
    while(ta<n){
        if(ta-he+1>=l&&RMQ(he,ta,1)-RMQ(he,ta,0)<=s&&(!he||dp[he-1]!=INF)){
            //printf("L=%d R=%d max=%d min=%d\n",he,ta,RMQ(he,ta,1),RMQ(he,ta,0));
            if(!he) dp[ta]=1;
            else    dp[ta]=min(dp[ta],dp[he-1]+1);
            ta++;
        }
        else if(ta-he+1<l) ta++;
        else he++;
    }
    //debug();
    if(dp[n-1]==INF) printf("-1\n");
    else    printf("%d\n",dp[n-1]);
}

int main()
{
    scanf("%d%d%d",&n,&s,&l);
    for(int i=0;i<n;i++) scanf("%d",&a[i]);
    solve();
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值