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 }
单调队列+尺取

相关文章:

  • 2022-12-23
  • 2022-12-23
  • 2021-05-30
  • 2021-10-24
  • 2022-12-23
  • 2021-04-20
  • 2021-10-23
  • 2021-06-18
猜你喜欢
  • 2021-06-08
  • 2021-07-22
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2021-05-26
  • 2022-02-16
相关资源
相似解决方案