您的位置:首页 > 其它

bzoj2096 pilots

2016-07-18 17:32 204 查看
Description

Tz又耍畸形了!!他要当飞行员,他拿到了一个飞行员测试难度序列,他设定了一个难度差的最大值,在序列中他想找到一个最长的子串,任意两个难度差不会超过他设定的最大值。耍畸形一个人是不行的,于是他找到了你。

Input

输入:第一行两个有空格隔开的整数k(0<=k<=2000,000,000),n(1<=n<=3000,000),k代表Tz设定的最大值,n代表难度序列的长度。第二行为n个由空格隔开的整数ai(1<=ai<=2000,000,000),表示难度序列。

Output

输出:最大的字串长度。

Sample Input

3 9

5 1 3 5 8 6 6 9 10

Sample Output

4

(有两个子串的长度为4: 5, 8, 6, 6 和8, 6, 6, 9.最长子串的长度就是4)

题解:

判断区间内的每一对值之差<
k
,则相当于区间的最大值最小值之差
<k
。很快的能想到,维护一个单调递增序列q1[]和一个单调递减序列q2[],定义这个区间的最大最小值,如果>k,则找队列中的下一个,相减与k作比较。个人认为最复杂的是区间的范围。代码中t是区间左端点,开始t=1,因此从第一个数开始。维护一个单调递减序列,一个递增,当两个队首差>k。那么在此之前区间最大最小之差<=k的情况区间从t=1到当前位置长度已经到达最大值。此时,只能去改变左端点,左端点要改变到哪里呢?肯定是两个队首对应的位置较小的一个的下一个,从这里算作新区间的左端点。

例如:

3 4 5 1 4 6

i到达5时,两个队列为:

q1[]={5}的下标(里面应该是5的下标,方便看)

q2[]={3 4 5}的下标

ans=3(i)-1(t)+1=3;

这时再进一个1

那么

q1[]={5 1}的下标

q2[]={1}的下标

队首大于3

执行while(),q1[]={1}的下标,直到不大于k,这时t=1的下标

q1[]={1}

q2[]={1}

那么ans=max(3,4-4+1)=3;

加入4,

q1[]={4};

q2[]={1 4};

ans=max(3,2)=3

加入6

q1[]={6}

q2[]={1 4 6}

6-1>k

while(),6-3<=k

ans=max(3,6(i)-5(t)+1)=3;

因此,最终连续的是,3 4 5最长。

因此,最难想的是如何去定义这个区间的左右端点,一旦q1首-q2首>k的时候,要把这两个值分开,因此找最小的下一个为新区间的左端点。至于为什么不改变右端点,因为此刻能到达的右端点就是最长区间了,如果要往左移动,只会使区间变得更短。

#include<iostream>
#include<cstdio>
using namespace std;
int ans=1,k,n,a[1000005],q1[1000005],q2[1000005];
void work()
{
int l1=1,l2=1,r1=0,r2=0,t=1;//一开始没有赋值,导致错误
for(int i=1;i<=n;i++)
{
while(l1<=r1 && a[i]>=a[q1[r1]]) r1--;//维护一个单调递减的序列
while(l2<=r2 && a[i]<=a[q2[r2]]) r2--;//维护一个单调递增的序列
q1[++r1]=i;q2[++r2]=i;

while(a[q1[l1]]-a[q2[l2]]>k)
{
if(q1[l1]<q2[l2]) t=q1[l1]+1,l1++;
else t=q2[l2]+1,l2++;
}
//t是什么?用来记录子串的开头下标。看q1[l1]和q2[l2]的最小值,然后把最小值+1;

ans=max(ans,i-t+1);
//只要加入一个数,都会计算ans,一开始理解成了上一步的while运算之后才计算ans.
}
}
int main()
{
cin>>k>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
work();
cout<<ans<<endl;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: