欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

牛客 最大最小(单调栈,区间最大大于最小两倍)

程序员文章站 2022-07-08 17:02:10
...

牛客 最大最小(单调栈,区间最大大于最小两倍)

思路:
首先必须明确单调栈的最主要的作用,就是找到每个数左边右边第一个小于大于他的数,如果配上倍增或者二分可以找到小于大于他的第k大数。所有用单调栈的题目都得思考怎么利用这个性质来套题目。

  1. 对于本题,我们可以找到每个数右边第一个大于等于其两倍的数R2[i]R2[i],小于等于其一半的数R3[i]R3[i]
  2. 可以找到以每个数为左端点的可行区间的数目,也就是n1max(R2[i],R3[i])+1n-1-max(R2[i],R3[i])+1
  3. 但此时有一些数的可行区间没算出来,但他可以与后面的数连接在一起构成可行区间。
  4. 可以想到如果一个区间可行,则把这个区间扩大仍然可行。所以我们还可以维护b[i]代表以第ii个数为左端点的可行区间数目,则可以有b[i]=max(b[i],b[i+1]b[i]=max(b[i],b[i+1]。也就是用有值的点来更新没有更新过的点,这个套路也很常见了。
  5. 因为每次只考虑以每个数为左端点的可行区间数目,所以可以保证不会算重。
typedef long long ll;
using namespace std;

const int maxn = 1e5;
const int INF = 0x3f3f3f3f;

struct STK {
    int id,v;
}stk[maxn];
int a[maxn],b[maxn];
int R2[maxn]; //右边第一个大于等于其两倍的数
int R3[maxn]; //右边第一个小于等于其1/2的数

int num[maxn];

int Bin(int top,int v) {
    stk[0].v = 1e9;
    stk[0].id = -1;
    int l = 0,r = top;
    int ans = 0;
    while(l <= r) {
        int mid = (l + r) >> 1;
        if(stk[mid].v >= v * 2) {
            l = mid + 1;
            ans = mid;
        } else {
            r = mid - 1;
        }
    }
    return ans;
}

int Bin2(int top,int v) {
    stk[0].v = -1e9;
    stk[0].id = -1;
    int l = 0,r = top;
    int ans = 0;
    while(l <= r) {
        int mid = (l + r) >> 1;
        if(stk[mid].v * 2 <= v) {
            l = mid + 1;
            ans = mid;
        } else {
            r = mid - 1;
        }
    }
    return ans;
}
class Solution {
public:
    /**
     *
     * @param array int整型一维数组 array
     * @param arrayLen int array数组长度
     * @return long长整型
     */
    long long MaxMin(int* array, int arrayLen) {
        // write code here
        int n = arrayLen;
        
        for(int i = 0;i < n;i++) {
            R2[i] = R3[i] = n;
            a[i] = array[i];
        }
        
        int top = 0;
        for(int i = n - 1;i >= 0;i--) { //单调递增队列,维护R3
            while(top && stk[top].v > a[i]) {
                top--;
            }
            //        printf("%d \n",top);
            R3[i] = stk[Bin2(top,a[i])].id;
            if(R3[i] == -1) R3[i] = n;
            stk[++top] = {i,a[i]};
        }
        
        top = 0;
        for(int i = n - 1;i >= 0;i--) { //单调递减队列,维护R2
            while(top && stk[top].v < a[i]) {
                top--;
            }
            R2[i] = stk[Bin(top,a[i])].id;
            if(R2[i] == -1) R2[i] = n;
            stk[++top] = {i,a[i]};
        }
        
        ll ans = 0;
        for(int i = 0;i < n;i++) {
            ll r = min(R3[i],R2[i]);
            b[i] = (n - 1 - r + 1);
        }
        for(int i = n - 2;i >= 0;i--) {
            b[i] = max(b[i],b[i + 1]);
        }
        for(int i = 0;i < n;i++) {
            ans += b[i];
        }
        return ans;
    }
};