Codeforces Round #278 (Div. 1) B

B. Strip


        题意:n个数,需要把他们切割成尽量少的部分,每部分连续,长度至少为l,其中最大数与最小数的差不能超过s。

        思路:首先,预处理每个区间内的最大数和最小数(spare table),然后就可以用dp的方法解决这个问题。dp的时候,利用预处理,对每个位置i找到以i为结尾,最左边能在哪个地方切割(二分查找,位置记为j),所以以i结尾合法的切割范围就是j~i-l。如果在位置k切割,状态转移就是dp(i)=dp(k)+1。我们要做的就是从j~i-l转移到i,这个过程需要用线段树优化,不然会T。。

#include <iostream>  
#include <stdio.h>  
#include <string.h>  
#include <algorithm>  
#include <queue>  
#include <map>  
#include <vector>  
#include <set>  
#include <string>  
#include <math.h>  
using namespace std;  
  
const int maxn=100010;  
const int INF=1e9+10;  
  
int a[maxn];  
int MIN[maxn][20];  
int MAX[maxn][20];  
int dp[maxn];  
  
int getmin(int l,int r){  
    int d=r-l+1;  
    int k=-1;  
    while(d){  
        k++;  
        d>>=1;  
    }  
    return min(MIN[l][k],MIN[r-(1<<k)+1][k]);  
}  
  
int getmax(int l,int r){  
    int d=r-l+1;  
    int k=-1;  
    while(d){  
        k++;  
        d>>=1;  
    }  
    return max(MAX[l][k],MAX[r-(1<<k)+1][k]);  
}  
  
struct node{  
    int l,r;  
    int val;  
}tree[maxn<<2];  
  
void build_tree(int n,int l,int r){  
    tree[n].l=l; tree[n].r=r;  
    tree[n].val=INF;  
    if(l==r)return;  
    int mid=(l+r)>>1;  
    build_tree(n<<1,l,mid);  
    build_tree((n<<1)|1,mid+1,r);  
}  
  
inline void push_up(int n){  
    tree[n].val=min(tree[n<<1].val,tree[(n<<1)|1].val);  
}  
  
void update(int n,int pos,int v){  
    if(tree[n].l==tree[n].r){  
        tree[n].val=v;  
        return;  
    }  
    int mid=(tree[n].l+tree[n].r)>>1;  
    if(pos<=mid) update(n<<1,pos,v);  
    else update((n<<1)|1,pos,v);  
    push_up(n);  
}  
  
int query(int n,int l,int r){  
    if(tree[n].l==l&&tree[n].r==r){  
        return tree[n].val;  
    }  
    int mid=(tree[n].l+tree[n].r)>>1;  
    if(r<=mid){  
        return query(n<<1,l,r);  
    }else{  
        if(l>mid){  
            return query((n<<1)|1,l,r);  
        }else{  
            return min( query(n<<1,l,mid) , query((n<<1)|1,mid+1,r) );  
        }  
    }  
}  
  
int main(){  
    int n,s,L;  
    cin>>n>>s>>L;  
    for(int i=1;i<=n;i++){  
        scanf("%d",&a[i]);  
        MAX[i][0]=MIN[i][0]=a[i];  
    }  
    int tmp=1;  
    int k=0;  
    while(tmp<n){  
        tmp<<=1;  
        k++;  
    }  
    for(int j=1;j<=k;j++){  
        for(int i=1;i+(1<<j)-1<=n;i++){  
            MIN[i][j]=min(MIN[i][j-1],MIN[i+(1<<(j-1)) ][j-1]);  
            MAX[i][j]=max(MAX[i][j-1],MAX[i+(1<<(j-1)) ][j-1]);  
        }  
    }  
      
    for(int i=1;i<=n;i++)dp[i]=INF;  
      
    build_tree(1,0,n);  
    update(1,0,0);  
    for(int i=1;i<=n;i++){  
        int l=0,r=i-L;  
        int pos=-1;  
        while(l<=r){  
            int mid=(l+r)>>1;  
            if(getmax(mid+1,i)-getmin(mid+1,i)<=s){  
                pos=mid;  
                r=mid-1;  
            }else{  
                l=mid+1;  
            }  
        }  
        if(pos!=-1&&pos<=i-L){  
            int t=query(1,pos,i-L);  
            if(t>=INF)continue;  
            dp[i]=t+1;  
            update(1,i,dp[i]);  
        }  
    }  
    if(dp[n]>=INF) cout<<-1<<endl;  
    else cout<<dp[n]<<endl;  
    return 0;  
}  


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值