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;
} 
京公网安备 11010502036488号