当前位置: 代码迷 >> 综合 >> 【POI2010】BZOJ2096 pilots
  详细解决方案

【POI2010】BZOJ2096 pilots

热度:86   发布时间:2024-01-13 11:54:09.0

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 输出:最大的字串长度。

从左往右扫描的时候,最大值不会变小,最小值不会变大,所以现在被舍弃的、太大的最大值和太小的最小值,以后也不会被用到。可以考虑单调队列。
用两个队列分别维护有用的最大值和最小值,其中最大值序列递减,最小值序列递增,保证每个元素都是从他往后整个序列的最大、小值。向右扫描的时候,首先在队尾出队并将该点入队使之仍然满足递增、递减性质。然后在队头出队使之满足差不超过k的性质,具体做法是将两个队头较靠前的元素出队,并且更新答案【左端点】所在的位置。最后更新答案。
注意细节:
1.最开始答案的左端点为1。
2.只有当扫描到的元素严格大于、小于队尾时才将队尾出队,因为等于时队尾比当前元素更优。

#include<cstdio>
#include<cstring>
int min(int x,int y)
{return x<y?x:y;
}
int max(int x,int y)
{return x>y?x:y;
}
int q_min[3000010],q_max[3000010],a[3000010];
int main()
{int i,j,k,m,n,p,q,x,y,z,h_max,t_max,h_min,t_min,ans=0;scanf("%d%d",&k,&n);for (i=1;i<=n;i++)scanf("%d",&a[i]);h_max=h_min=1;t_max=t_min=0;p=1;for (i=1;i<=n;i++){while (h_max<=t_max&&a[q_max[t_max]]<a[i]) t_max--;q_max[++t_max]=i;while (h_min<=t_min&&a[q_min[t_min]]>a[i]) t_min--;q_min[++t_min]=i;while (a[q_max[h_max]]-a[q_min[h_min]]>k){if (q_max[h_max]<q_min[h_min]){p=q_max[h_max]+1;h_max++;}else{p=q_min[h_min]+1;h_min++;}}ans=max(ans,i-p+1);}printf("%d\n",ans);
}