【单调队列+尺取】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\n",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\n",l-1); 67 } 68 return 0; 69 }
不知道这个二分答案,然后用单调队列判断可行性为啥是挂的orz,我感觉没错.....希望哪位仁兄看到了帮忙指点一下,小弟不尽感激~