HDU 5289 Assignment (尺取+RMQ)

Description

给出一段长度为\(n\)的序列,问最大值与最小值之差小于\(k\)的连续序列有多少个。

Input

第一行给出用例组数\(T\),对于每组用例,第一行给出\(n\)\(k\),第二行给出\(n\)个数表示这个序列。\(1 \leqslant n \leqslant 10^5\)

Output

对于每组用例输出一个整数,表示满足条件的序列数量。

Sample Input

2
4 2
3 1 2 4
10 5
0 3 4 5 2 1 6 7 8 9

Sample Output

5
28

Solution

满足条件的连续区间,典型的尺取问题。限制条件是上限,区间最大值和最小值之差要小于\(k\)。左端点依次推进,对于每个左端点,右端点向右找到能延伸的远位置,此时这段区间长度就这个左端点的贡献。区间最值可用ST快速查询。

Code

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int INF = 0x7f7f7f7f;
const int N = 1e5 + 10;

int a[N];

int dpmax[N][20], dpmin[N][20], lg[N];
void ST(int n)
{
	lg[0] = -1;
	for (int i = 1; i <= n; i++)
	{
		lg[i] = ((i & (i - 1))) == 0 ? lg[i - 1] + 1 : lg[i - 1];
		dpmax[i][0] = dpmin[i][0] = a[i];
	}
	for (int j = 1; j <= lg[n]; j++)
		for (int i = 1; i + (1 << j) - 1 <= n; i++)
		{
			dpmax[i][j] = max(dpmax[i][j - 1], dpmax[i + (1 << (j - 1))][j - 1]);
			dpmin[i][j] = min(dpmin[i][j - 1], dpmin[i + (1 << (j - 1))][j - 1]);
		}
}
int rmq(int l, int r)
{
	int k = lg[r - l + 1];
	int maxx = max(dpmax[l][k], dpmax[r - (1 << k) + 1][k]);
	int minx = min(dpmin[l][k], dpmin[r - (1 << k) + 1][k]);
	return maxx - minx;
}

int main()
{
	int T;
	scanf("%d", &T);
	while (T--)
	{
		int n, k;
		scanf("%d%d", &n, &k);
		for (int i = 1; i <= n; i++) scanf("%d", a + i);
		ST(n);
		ll ans = 0;
		for (int l = 1, r = 1; l <= n; l++)
		{
			while (r <= n && rmq(l, r) < k) r++;
			ans += r - l;
		}
		printf("%lld\n", ans);
	}
	return 0;
}

http://acm.hdu.edu.cn/showproblem.php?pid=5289

posted @ 2017-08-13 09:40  达达Mr_X  阅读(172)  评论(0)    收藏  举报