最大最小(区间问题)
最大最小
题目描述:
牛妹有一个数组array,她想知道里面有多少个区间满足区间最大值大于等于区间最小值的两倍。
链接:https://ac.nowcoder.com/acm/contest/6778/C
来源:牛客网
输入:
给定A数组 长度n 0<n<=100000;0<A[i]<=1e9;
输出:
返回满足条件的区间个数
输入: 2 1 2 输出 1 解释:只有区间[1,2]满足。
解法:
- 首先用一个数组储存A[i]前面第一个比他小的值的下标。即pr[i]
- 再用一个数组储存A[i]之后第一个比他小的值的下标。即nt[i]
- 然后用同样的做法,但是中间需要以2分查找的方式找到在i前面第一个大于2倍A[i]的下标。
- 最后对每一个pr1[i]=max(pr[i],pr[i]) nt1[i]=min(nt[i],nt1[i]):这一步可以在pr1和nt1生成时候做。
- 最后循环n次。ans+=(i-pr[i])*(nt[i]-i)-(i-pr1[i])*(nt1[i]-i)。
解析:
为什么要求第一个前后最小啦?
由于求区间最大最小。求左右最小也就是该数值能成为最小的作用的区域。也就是pr[i]~nt[i]区间A[i]都是最小。
既然我们求得了一个值在最小作用的区域。第二步那就是在此区域内pr[i]~nt[i]大于A[i]2倍的值。用2分找到。
左边区间即为1~pr1[i]。右边即为nt1[i]~n;
由于区间最小区域要和大于两倍A[i]的区域重叠才能有解。(不重叠你就算是区间最最小,没有大于2倍也没有区间满足)
所以会有第4步。不过做的时候可以直接就求出区间重叠部分。(代码也是这么写的)
最后计算就是:包含i在内所有的区间减去不满足条件的区间。比如pr1[i]-i之间就不存在大于A[i]2倍的值。所以直接减去。
代码:
class Solution { typedef long long ll; public: /** * * @param array int整型一维数组 array * @param arrayLen int array数组长度 * @return long长整型 */ int nt[100005],nt1[100005],pr[100005],pr1[100005],s[100005]; long long MaxMin(int* array, int arrayLen) { // write code here int n = arrayLen,*a=array; int t=0; s[t]=-1; for(int i=0;i<n;i++){ while(t&&a[s[t]]>a[i]) t--; pr[i]=s[t]; s[++t]=i; } s[t=0]=n; for(int i=n-1;~i;i--){ while(t&&a[s[t]]>=a[i]) t--; nt[i]=s[t]; s[++t]=i; } s[t=0]=-1; for(int i=0;i<n;i++){ while(t&&a[s[t]]<=a[i]) t--; s[++t]=i; int l=0,r=t; while(l+1<r){ int mid = (l+r)>>1; if(a[s[mid]]<(a[i]<<1)) r=mid; else l=mid; } pr1[i]=max(pr[i],s[l]); } s[t=0]=n; for(int i=n-1;~i;i--){ while(t&&a[s[t]]<=a[i]) t--; s[++t]=i; int l=0,r=t; while(l+1<r){ int mid = (l+r)>>1; if(a[s[mid]]<(a[i]<<1)) r=mid; else l=mid; } nt1[i]=min(nt[i],s[l]); } ll ans=0,p=1; for(int i=0;i<n;i++) ans+=p*(i-pr[i])*(nt[i]-i)-p*(i-pr1[i])*(nt1[i]-i); return ans; } };

浙公网安备 33010602011771号