Problem Description
Tom owns a company and he is the boss. There are n staffs which are numbered from 1 to n in this company, and every staff has a ability. Now, Tom is going to assign a special task to some staffs who were in the same group. In a group, the difference of the ability of any two staff is less than k, and their numbers are continuous. Tom want to know the number of groups like this.
Input
In the first line a number T indicates the number of test cases. Then
for each case the first line contain 2 numbers n, k
(1<=n<=100000, 0<k<=10^9),indicate the company has n
persons, k means the maximum difference between abilities of staff in a
group is less than k. The second line contains n
integers:a[1],a[2],…,a[n](0<=a[i]<=10^9),indicate the i-th staff’s
ability.
Output
For each test,output the number of groups.
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
Hint
First Sample, the satisfied groups include:[1,1]、[2,2]、[3,3]、[4,4] 、[2,3]
题目大意是求满足下列条件的子区间的个数:
对于子区间[L, R]内的任意两个元素的差值小于k。
首先可以肯定的是,以An起始的区间肯定是[n, n]。
然后以An-1起始的区间最长是[n-1, n],然后考虑需不需要把区间的右值减小,也就是考虑An和An-1的差值是否小于k。假设最终的区间为[n-1, d(n-1)]。
于是对于以An-2起始的区间,自然最长是[n-2, d(n-1)],然后考虑需不需要把区间的右值减小,也就是考虑这个区间内是否存在某个值与An-2的差值大于等于k。
以此类推,以Ai起始的区间应为[i, min(d(i+1), p)],其中p是i右侧最后一个满足与Ai差值小于k的数的脚标。
于是采用线段树记录区间的最大值和最小值,就能查询出任意[i, n]区间里第一个满足与Ai差值大于等于k的值的位置x,然后x-1即为最后一个满足与Ai差值小于k的数的脚标。
(此处采用ans记录x,ans为-1表示没找到,自然x-1就是n)
代码:
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <set> #include <map> #include <queue> #include <string> #include <algorithm> #define LL long long using namespace std; int n, k; int a[100005], ans; int d[100005]; //线段树 //区间每点增值,求区间和 const int maxn = 100005; struct node { int lt, rt; LL mi, ma; }tree[4*maxn]; //向上更新 void pushUp(int id) { tree[id].mi = min(tree[id<<1].mi, tree[id<<1|1].mi); tree[id].ma = max(tree[id<<1].ma, tree[id<<1|1].ma); } //建立线段树 void build(int lt, int rt, int id) { tree[id].lt = lt; tree[id].rt = rt; tree[id].mi = 0;//每段的初值,根据题目要求 tree[id].ma = 0; if (lt == rt) { tree[id].mi = tree[id].ma = a[lt]; return; } int mid = (lt + rt) >> 1; build(lt, mid, id<<1); build(mid+1, rt, id<<1|1); pushUp(id); } void query(int lt, int rt, int id, int v) { if (tree[id].lt == tree[id].rt) { if (abs(tree[id].mi-v) >= k) { if (ans == -1 || ans > tree[id].lt) ans = tree[id].lt; } return; } int mid = (tree[id].lt + tree[id].rt) >> 1; if (lt <= mid) if (abs(tree[id<<1].mi-v) >= k || abs(tree[id<<1].ma-v) >= k) query(lt, rt, id<<1, v); if (ans == -1 && rt > mid) if (abs(tree[id<<1|1].mi-v) >= k || abs(tree[id<<1|1].ma-v) >= k) query(lt, rt, id<<1|1, v); } void input() { scanf("%d%d", &n, &k); for (int i = 1; i <= n; ++i) { scanf("%d", &a[i]); } build(1, n, 1); } void work() { LL sum = 1; d[n] = n; for (int i = n-1; i >= 1; --i) { ans = -1; query(i, n, 1, a[i]); if (ans != -1) ans -= 1; else ans = n; d[i] = min(ans, d[i+1]); sum += d[i]-i+1; } printf("%lld\n", sum); } int main() { //freopen("test.txt", "r", stdin); int T; scanf("%d", &T); for (int times = 0; times < T; ++times) { input(); work(); } return 0; }