acm.hdu.edu.cn/showproblem.php?pid=3530
【题意】
- 给定一个长度为n的序列,问这个序列满足最大值和最小值的差在[m,k]的范围内的最长子区间是多长?
【思路】
- 对于序列中特定的位置j,我们固定右端j考察左端i,发现[i,j]内的最大值随i的增大而非严格递减
- 对于序列中特定的位置j,我们固定右端j考察左端i,发现[i,j]内的最小值随i的增大而非严格递增
- 所以[i,j]内最大值与最小值的差随i的增大而递减
- 对于序列中特定的位置i,我们固定左端i考察右端j,发现[i,j]内的最大值随j的增大而非严格递增
- 对于序列中特定的位置i,我们固定左端i考察右端j,发现[i,j]内的最小值随j的增大而非严格递减
- 所以[i,j]内最大值与最小值的差随i的增大而递增
- 所以我们可以尺取,即如位置j对应的左端为l,那么i+1的从l以后找
- 两个单调队列mx和mn分别维护最大值和最小值
- mx中存放的是a中数的下标(单调递增),对应的a中的数是单调递减
- mn中存放的是a中数的下标(单调递增),对应的a中的数是单调递增
- 当a[mx[lx]]-a[mn[ln]]>k时,出队列的是mx[lx]和mn[ln]较小的一个,这样才能保证最长子区间
- 当a[mx[lx]]-a[mn[ln]]<m时,不需做任何操作,因为差是随左端点的增大而递减的,如果a[mx[lx]]-a[mn[ln]]<m,只能不更新ans
【AC】
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 int n,m,k; 5 const int maxn=1e5+2; 6 int a[maxn]; 7 int mx[maxn],lx,rx; 8 int mn[maxn],ln,rn; 9 int l,ans; 10 int main() 11 { 12 while(~scanf("%d%d%d",&n,&m,&k)) 13 { 14 for(int i=1;i<=n;i++) scanf("%d",&a[i]); 15 lx=ln=1;rx=rn=0;l=0; 16 ans=0; 17 for(int i=1;i<=n;i++) 18 { 19 while(lx<=rx&&a[mx[rx]]<=a[i]) rx--; 20 mx[++rx]=i; 21 while(ln<=rn&&a[mn[rn]]>=a[i]) rn--; 22 mn[++rn]=i; 23 while(lx<=rx&&ln<=rn&&a[mx[lx]]-a[mn[ln]]>k) 24 { 25 if(mx[lx]>=mn[ln]) 26 { 27 l=mn[ln]; 28 ln++; 29 } 30 else 31 { 32 l=mx[lx]; 33 lx++; 34 } 35 } 36 if(a[mx[lx]]-a[mn[ln]]>=m) 37 ans=max(ans,i-l); 38 } 39 printf("%d\n",ans); 40 } 41 return 0; 42 }