Arithmetic Progression
给定每个元素互不相同的序列 ,求排序后能构成等差数列的区间个数。
结论:对于元素互不相同的序列 ,其排序后是公差为 等差数列的充要条件是 且 。
证明:
必要性:
设 且 排序后为等差数列,则 是 的一个排列,所以 。
根据辗转相减法:
。
充分性:
不妨设 ,。
设 ,则 ,所以 ,为等差数列。
于是问题转化为求 的区间个数。
由上面的证明不难发现 ,所以用线段树维护这个值的最小值以及最小值的个数。
每次移动右端点时,极差只需修改一个区间,但每个点的 都可能被修改。
考虑一个点作为左端点的区间 被修改的次数,发现每次修改至少会折半,所以最多修改 次,于是在线段树上暴力修改即可。
不需要 zkw 跑得也很快!
// Author: HolyK // Created: Wed Jul 21 12:59:35 2021 #include <bits/stdc++.h> #define dbg(a...) fprintf(stderr, a) template <class T, class U> inline bool smin(T &x, const U &y) { return y < x ? x = y, 1 : 0; } template <class T, class U> inline bool smax(T &x, const U &y) { return x < y ? x = y, 1 : 0; } using LL = long long; using PII = std::pair<int, int>; constexpr int N(1e5 + 5); int n, a[N]; #define ls o << 1 #define rs o << 1 | 1 LL min[N << 2], tag[N << 2]; int cnt[N << 2], gcd[N << 2]; void pushup(int o) { min[o] = std::min(min[ls], min[rs]); cnt[o] = (min[ls] == min[o] ? cnt[ls] : 0) + (min[rs] == min[o] ? cnt[rs] : 0); min[o] += tag[o]; gcd[o] = gcd[ls] == gcd[rs] ? gcd[ls] : -1; } void update(int o, int l, int r, int x, int y, int z) { if (gcd[o] > 0 && z % gcd[o] == 0) { min[o] -= gcd[o]; tag[o] -= gcd[o]; return; } if (l == r) { int t = gcd[o]; gcd[o] = std::gcd(gcd[o], z); min[o] -= LL(gcd[o] - t) * (y - l) + gcd[o]; cnt[o] = 1; return; } int m = l + r >> 1; if (x <= m) update(ls, l, m, x, y, z); if (y > m) update(rs, m + 1, r, x, y, z); pushup(o); } void add(int o, int l, int r, int x, int y, int z) { if (x <= l && r <= y) { min[o] += z; tag[o] += z; return; } int m = l + r >> 1; if (x <= m) add(ls, l, m, x, y, z); if (y > m) add(rs, m + 1, r, x, y, z); pushup(o); } int ask(int o, int l, int r, int x, int y, int z) { if (y < l || r < x) return 0; if (x <= l && r <= y) return min[o] + z == 0 ? cnt[o] : 0; int m = l + r >> 1; z += tag[o]; return ask(ls, l, m, x, y, z) + ask(rs, m + 1, r, x, y, z); } int s1[N], s2[N], t1, t2; // min, max int main() { std::ios::sync_with_stdio(false); std::cin.tie(nullptr); int t; std::cin >> t; while (t--) { std::cin >> n; memset(min, 0, n + 1 << 5); memset(tag, 0, n + 1 << 5); memset(cnt, 0, n + 1 << 4); memset(gcd, 0, n + 1 << 4); for (int i = 1; i <= n; i++) { std::cin >> a[i]; } LL ans = n; t1 = t2 = s1[1] = s2[1] = 1; for (int i = 2; i <= n; i++) { while (t1 && a[s1[t1]] > a[i]) { add(1, 1, n, s1[t1 - 1] + 1, s1[t1], a[s1[t1]]); t1--; } while (t2 && a[s2[t2]] < a[i]) { add(1, 1, n, s2[t2 - 1] + 1, s2[t2], -a[s2[t2]]); t2--; } add(1, 1, n, s1[t1] + 1, i, -a[i]); add(1, 1, n, s2[t2] + 1, i, a[i]); s1[++t1] = s2[++t2] = i; update(1, 1, n, 1, i - 1, a[i] - a[i - 1]); ans += ask(1, 1, n, 1, i - 1, 0); } std::cout << ans << "\n"; } return 0; }