题目描述
Given nn_{}n integers a1,a2,⋯ ,ana_1,a_2,\cdots,a_na1,a2,⋯,an and mm_{}m queries. For each query, you are given a const kk_{}k and you should determine how many different pairs (l,r)(l,r)_{}(l,r) are there meeting the condition that the range of the subsequence al,al+1,⋯ ,ara_l,a_{l+1},\cdots,a_ral,al+1,⋯,ar is strictly greater than kk_{}k.
Note: the range of a sequence equals the difference between the maximum and the minimum of the sequence.
输入描述:
The first line contains two integers n,m (1≤n≤105,1≤m≤200)n,m\,(1 \le n \le 10^5, 1 \le m \le 200)n,m(1≤n≤105,1≤m≤200), denoting the number of given integers and the number of queries respectively. The second line contains n integers a1,a2,⋯ ,an (1≤ai≤109)a_1, a_2, \cdots, a_n\,(1 \le a_i \le 10^9)a1,a2,⋯,an(1≤ai≤109), denoting the given integers. Next m lines each contains one integer k (1≤k≤109)k\,(1 \le k \le 10^9)k(1≤k≤109), denoting the queries.
输出描述:
Print mm_{}m lines each contains one integer, denoting the answers.
示例1
输入
5 1 1 2 3 4 5 2
输出
3
说明
There are three pairs, (1,4),(1,5),(2,5)(1,4),(1,5),(2,5)_{}(1,4),(1,5),(2,5), which meet the condition.
题意: 给出长度为n的数组以及m次询问,每次询问给出一个值k,求数组中满足区间最大值减区间最小值严格大于k的区间个数。
分析: 一开始想到的是O(nmlogn)做法,用st表预处理出来区间max和区间min,对于每个询问先O(n)枚举区间左端点,由于区间max-区间min具有单调性,所以可以二分区间右端点,然后贡献加上后面的区间个数。
不过时间卡的比较死,这样做会TLE,后来考虑了一下必须优化到O(nm),于是尝试将二分换成了双指针,双指针的过程就像求区间和大于k的区间个数一样,理论上这个复杂度肯定不会T了,但它还是T了,再次检查代码后发现st表求区间最值时会多次调用log2库函数,这个函数是比较慢的,于是将log2(i)的值预处理到数组中就可以了。
具体代码如下:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
const int N=1e5+10;
int fx[N][21],fn[N][21];
int Log2[N];
int mx(int l,int r)
{
int t=Log2[r-l+1];
return max(fx[l][t],fx[r+1-(1<<t)][t]);
}
int mn(int l,int r)
{
int t=Log2[r-l+1];
return min(fn[l][t],fn[r+1-(1<<t)][t]);
}
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
scanf("%d",&fx[i][0]);
fn[i][0]=fx[i][0];
Log2[i] = log2(i);
}
for(int i=1;i<=20;i++)
for(int j=1;j+(1<<i)-1<=n;j++)
{
fx[j][i]=max(fx[j][i-1],fx[j+(1<<(i-1))][i-1]);
fn[j][i]=min(fn[j][i-1],fn[j+(1<<(i-1))][i-1]);
}
while(m--)
{
int k;
scanf("%d",&k);
long long ans = 0;
int r = 1;
for(int l=1;l<=n;l++)
{
while(r <= n && mx(l, r)-mn(l, r) <= k) r++;
if(r > n) break;
ans += n-r+1;
}
printf("%lld\n",ans);
}
return 0;
}