【单调队列+尺取】HDU 3530 Subsequence

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
",ans);
40     }
41     return 0;
42 }
单调队列+尺取

 【疑问】

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 int n,m,k;
 5 const int maxn=1e5+3;
 6 int a[maxn];
 7 int mx[maxn];
 8 int mn[maxn];
 9 struct node
10 {
11     int x;
12     int pos;
13 }q[maxn];
14 bool judge(int mid)
15 {
16     memset(q,0,sizeof(q));
17     memset(mx,0,sizeof(mx));
18     memset(mn,0,sizeof(mn));
19     int head=1,tail=0;
20     for(int i=1;i<=n;i++)
21     {
22         while(tail>=head&&q[tail].x<a[i]) tail--;
23         q[++tail].x=a[i];q[tail].pos=i;
24         while(q[head].pos<=i-mid) head++;
25         if(i>=mid) mx[i]=q[head].x;
26     }
27     
28     head=1,tail=0;
29     for(int i=1;i<=n;i++)
30     {
31         while(tail>=head&&q[tail].x>a[i]) tail--;
32         q[++tail].x=a[i];q[tail].pos=i;
33         while(q[head].pos<=i-mid) head++;
34         if(i>=mid) mn[i]=q[head].x;
35     }
36     for(int i=mid;i<=n;i++)
37     {
38         if(mx[i]-mn[i]>=m&&mx[i]-mn[i]<=k)
39         {
40             return true;
41         }
42     }
43     return false;
44 }
45 int main()
46 {
47     while(scanf("%d%d%d",&n,&m,&k)!=EOF)
48     {
49         for(int i=1;i<=n;i++)
50         {
51             scanf("%d",&a[i]);
52         }
53         int l=1,r=n;
54         while(l<=r)
55         {
56             int mid=(l+r)>>1;
57             if(judge(mid))
58             {
59                 l=mid+1;
60             }
61             else
62             {
63                 r=mid-1;
64             }
65         }
66         printf("%d
",l-1);
67     }
68     return 0;
69 }
WA

不知道这个二分答案,然后用单调队列判断可行性为啥是挂的orz,我感觉没错.....希望哪位仁兄看到了帮忙指点一下,小弟不尽感激~

原文地址:https://www.cnblogs.com/itcsl/p/7445748.html