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