MT3024 max=min

文章介绍了如何利用SparseTable数据结构和二分查找算法解决一个关于数组中单调区间的问题,通过预计算区间最值来找到满足特定条件的最大连续子数组长度。
摘要由CSDN通过智能技术生成

样例 1

输入:

6
1 2 3 2 1 4
6 7 1 2 3 2

输出:

2

备注

1≤n≤10^6,1≤ai​,bi​≤10^9。

思路:

(1)使用ST表。

ST表(Sparse Table)是一种用于高效处理区间查询的数据结构。它可以在O(1)的时间复杂度内回答某一区间的最值查询(最小值、最大值等)。ST表使用动态规划的思想,通过预处理的方式来快速计算出各个区间的最值。

(2)使用二分。因为此题具有单调性:随着放入数组的数越多,最大值一定非递减。最小值一定非递增。如果固定右端点r,则向左看,最大值从l->r呈阶梯状递增,最小值从l->r呈阶梯状递减,其中一定有交点使最小值=最大值。因为是阶梯状递增和递减,所以交点为横着的一条线段,即l的范围为此线段的范围。

所以每次枚举r,找l的范围并保存在答案中。找l的范围即找max=min,用二分来找。

此题代码:

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int n, a[N], b[N], mn[N][20], mx[N][20], Lg[N], l, r, mid, ans;
void pre()
{
    Lg[1] = 0;
    for (int i = 2; i <= n; i++)
    {
        Lg[i] = Lg[i >> 1] + 1;
    }
}
void ST_create()
{ // 创建ST表
    for (int i = 1; i <= n; i++)
    {
        mx[i][0] = a[i], mn[i][0] = b[i];
    }
    for (int j = 1; j <= Lg[n]; j++)
    {
        for (int i = 1; i <= n - (1 << j) + 1; i++)
        {
            mx[i][j] = max(mx[i][j - 1], mx[i + (1 << (j - 1))][j - 1]);
            mn[i][j] = min(mn[i][j - 1], mn[i + (1 << (j - 1))][j - 1]);
        }
    }
}
int ST_qmax(int l, int r)
{ // ST表求max
    int k = Lg[r - l + 1];
    return max(mx[l][k], mx[r - (1 << k) + 1][k]);
}
int ST_qmin(int l, int r)
{ // ST表求min
    int k = Lg[r - l + 1];
    return min(mn[l][k], mn[r - (1 << k) + 1][k]);
}

int findl(int i)
{ // l的范围左边界
    int l = 1, r = i, mid, ans = 0, minn, maxn;
    while (l <= r)
    {
        mid = (l + r) / 2;
        maxn = ST_qmax(mid, i), minn = ST_qmin(mid, i);
        if (maxn <= minn)
        {
            ans = mid, r = mid - 1;
        }
        else
            l = mid + 1;
    }
    return ans;
}
int findr(int i) // i为固定的枚举的右边界
{                // l的范围右边界
    int l = 1, r = i, mid, ans = 0, minn, maxn;
    while (l <= r) // 二分
    {
        mid = (l + r) / 2;
        maxn = ST_qmax(mid, i), minn = ST_qmin(mid, i);
        if (maxn >= minn)
        {
            ans = mid, l = mid + 1;
        }
        else
            r = mid - 1;
    }
    return ans;
}
int main()
{
    cin >> n;
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    for (int i = 1; i <= n; i++)
        cin >> b[i];
    pre();
    ST_create();
    for (int i = 1; i <= n; i++)
    { // 枚举右端点i
        // 对于每个i,求l的范围findl和findr
        int j = findl(i);
        if (j < 1 || j > i)
        {
            continue;
        }
        int k = findr(i);
        ans += k - j + 1;
    }
    cout << ans;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值