标签:
题目大意是求满足下列条件的子区间的个数:
对于子区间[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; }
ACM学习历程—HDU 5289 Assignment(线段树)
标签:
原文地址:http://www.cnblogs.com/andyqsmart/p/4665423.html