要求最大值最小值之差小于k的子序列的个数
本题有两种方法求解:
方法一:单调队列
暴力枚举区间右端点,然后利用左端点不减的性质解决题目。
具体就是使用两个单调队列分别维护最大最小值
上次用这个数据结构还是高一,当时还十分业余,这次用deque好好的实现了一下。
首先注意单调队列这种数据结构,利用单调性有着十分优秀的性质,合理利用可有大作用。
以本题的最大值优先队列维护区间最大值为例
扫描数组,如果当前数字比对手数字大,那么直接插入队头,否则,重点的是,我们不能就此放弃插入这个数字,反之,由于是从前向后扫描数组,那么当加入了目前队头这个最大值之后,比他小的那些元素就全都用不到了,因为这些都比最大值更靠前,所以应该从队尾把比当前元素小的元素全部弹出,然后加入当前元素在队尾
具体实现看代码
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <deque>
#define rep(i, j, k) for(int i = j; i <= k; i++)
#define maxn 100009
#define ll long long
using namespace std;
int n, k, a[maxn], MaxNum[maxn][21], MinNum[maxn][21];
ll ans = 0;
int main()
{
int Case;
scanf ("%d", &Case);
while (Case--)
{
deque <int> qma, qmi;
ans = 0;
scanf ("%d%d", &n, &k);
rep (i, 1, n)
scanf ("%d", &a[i]);
int l = 1;
rep (i, 1, n)
{
if (a[i] > qma.front () || qma.empty ())
qma.clear (), qma.push_front (a[i]);
else
{
while (qma.back () < a[i])
qma.pop_back ();
qma.push_back (a[i]);
}
if (a[i] < qmi.front () || qmi.empty ())
qmi.clear (), qmi.push_front (a[i]);
else
{
while (qmi.back () > a[i])
qmi.pop_back ();
qmi.push_back (a[i]);
}
int Max = qma.front (), Min = qmi.front ();
while (Max - Min >= k)
{
//printf ("---- %d %d %d\n", l, Max, Min);
if (a[l] == Max)
qma.pop_front (), Max = qma.front ();
if (a[l] == Min)
qmi.pop_front (), Min = qmi.front ();
l++;
}
//printf ("%d %d=%d ===%d %d\n", Max, Min, qmi.size (), l, i);
ans += (i - l + 1);
}
cout << ans << endl;
}
return 0;
}
方法二:
我们知道求区间最大最小值可以用logn做到的数据结构有很多
这个不涉及修改
当然就选择最简单的rmq,不多废话了
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#define rep(i, j, k) for(int i = j; i <= k; i++)
#define maxn 100009
#define ll long long
using namespace std;
int n, k, a[maxn], MaxNum[maxn][21], MinNum[maxn][21];
ll ans = 0;
void pre ()
{
rep (i, 1, n)
MaxNum[i][0] = MinNum[i][0] = a[i];
rep (j, 1, 20)
rep (i, 1, n)
if (i + (1 << j) - 1 <= n)
MaxNum[i][j] = max (MaxNum[i][j - 1], MaxNum[i + (1 << (j - 1))][j - 1]), MinNum[i][j] = min (MinNum[i][j - 1], MinNum[i + (1 << (j - 1))][j - 1]);
}
int ask_max (int l, int r)
{
int now = 0;
while (l + (1 << (now + 1)) - 1 <= r)
now++;
return max (MaxNum[l][now], MaxNum[r - (1 << now) + 1][now]);
}
int ask_min (int l, int r)
{
int now = 0;
while (l + (1 << (now + 1)) - 1 <= r)
now++;
return min (MinNum[l][now], MinNum[r - (1 << now) + 1][now]);
}
int main()
{
int Case;
scanf ("%d", &Case);
while (Case--)
{
ans = 0;
scanf ("%d%d", &n, &k);
rep (i, 1, n)
scanf ("%d", &a[i]);
pre ();
int now = 1;
rep (i, 1, n)
{
int Max, Min;
while (1)
{
Max = ask_max (now, i);
Min = ask_min (now, i);
//printf ("ask %d %d Max %d Min %d\n", i, now, Max, Min);
if (Max - Min < k)
break;
now++;
}
ll t = i - now + 1;
//printf ("%d %d========\n", now, i);
//ans += (t + 1) * t / 2;
ans += t;
}
cout << ans << endl;
}
return 0;
}