bzoj2096 pilots

来源:互联网 发布:关于地球的软件 编辑:程序博客网 时间:2024/05/29 16:03

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;}
0 0
原创粉丝点击