牛客编程巅峰赛S1第8场 - 王者 C-最大最小 (ST表+双指针)
链接:https://ac.nowcoder.com/acm/contest/6778/C
来源:牛客网
题目描述
牛妹有一个数组array,她想知道里面有多少个区间满足区间最大值大于等于区间最小值的两倍。
输入:
给定Array数组$ 1≤array.size≤10^5,1 leq array.size leq 10{5},1≤array.size≤105( ) 1≤array[i]≤1091 leq array[i] leq 10^{9}1≤array[i]≤109 $
输出:
返回满足条件的区间个数
思路:
先用ST表(O(n*logn))预处理一下,之后可以(O(1))查询区间最值。
然后我们枚举区间的左端点(mathit L),
对于每一个(mathit L),我们只需要找到一个最小的右端点(mathit R),使其(2 imessum_{i=L}^{i=R}min(a_i)leqsum_{i=L}^{i=R}max(a_i))
那么所有以(mathit L)为左端点的区间中,当右端点大于等于(mathit R)时就满足条件,所以贡献为(n-R+1)。
把所有的左端点区间的贡献加起来就是总答案。
代码:
class Solution {
public:
/**
*
* @param array int整型一维数组 array
* @param arrayLen int array数组长度
* @return long长整型
*/
int a[100010];
int l[100010];
int r[100010];
int st1[100010][25];//st表
int st2[100010][25];//st表
void init(int n)
{
for (int i = 0; i < n; i++)
{
st1[i][0] = a[i + 1];
st2[i][0] = a[i + 1];
}
for (int i = 1; (1 << i) <= n; i++)
{
for (int j = 0; j + (1 << i) - 1 < n; j++)
{
st1[j][i] = max(st1[j][i - 1], st1[j + (1 << (i - 1))][i - 1]);
st2[j][i] = min(st2[j][i - 1], st2[j + (1 << (i - 1))][i - 1]);
}
}
}
int querysmax(int l, int r)
{
l--;
r--;
int k = (int)(log((double)(r - l + 1)) / log(2.0));
return max(st1[l][k], st1[r - (1 << k) + 1][k]);
}
int queryemin(int l, int r)
{
l--;
r--;
int k = (int)(log((double)(r - l + 1)) / log(2.0));
return min(st2[l][k], st2[r - (1 << k) + 1][k]);
}
long long MaxMin(int* array, int arrayLen) {
// write code here
int n = arrayLen;
for (int i = 1; i <= n; ++i)
{
a[i] = array[i - 1];
}
init(n);
long long ans = 0ll;
int l = 1;
int r = 1;
for (l = 1; l <= n; ++l)
{
while (r < n && 1ll * queryemin(l, r) * 2 > querysmax(l, r))
{
r++;
}
if (1ll * queryemin(l, r) * 2 <= querysmax(l, r))
{
ans += (n - r + 1);
}
}
return ans;
}
};