题目大意
给你一个长度为 n n n 的数列 a a a ,数列中每个元素都不一样,问你存在多少个区间,这些区间内的数排序后是一个等差数列
解题思路
对于一个区间,如果这个区间内的数排序后的元素可以构成一个等差序列,那么一定满足下列性质
m
a
x
−
m
i
n
=
(
r
−
l
)
×
g
c
d
max - min = (r - l) \times gcd
max−min=(r−l)×gcd
其中
g
c
d
gcd
gcd 是区间差分绝对值的最大公约数
m
i
n
min
min 是区间的最小值
m
a
x
max
max 是区间的最大值
我们做这题的思想是,枚举右端点,用数据结构维护有多少个合法的左端点
我们用一个线段树来维护这个东西。线段树一个节点维护四个值
d
a
t
:
m
a
x
−
m
i
n
−
(
r
−
l
)
×
g
c
d
dat: max-min-(r-l)\times gcd
dat:max−min−(r−l)×gcd的最小值,右端点为我们枚举的右端点(因为这个式子的最小值为0,当这个值为0时,说明这是一个合法的区间)
c
n
t
:
cnt:
cnt:合法左端点的个数
a
d
d
:
add:
add:懒标记
g
:
g:
g:当前节点所代表区间的差分的绝对值的最大公约数
这样我们每次询问区间
[
1
,
r
]
[1, r]
[1,r] 就可以知道有多少个合法的区间
接下来我们考虑右端点往右移(即新加一个数)的情况。
新加入一个数,可能对之前的
m
i
n
min
min 和
m
a
x
max
max 有影响,为了解决这个问题,我们用单调栈解决。
我们对于
m
i
n
min
min 维护一个上升的单调栈,栈里面存下标
如果新加入的元素比栈顶大,那么直接入栈,它不会对之前区间的
m
i
n
min
min 产生影响。
如果新加入的元素小于等于栈顶元素,那么我们将区间
[
[
[ 栈顶第二个元素+1, 栈顶元素
]
]
] 都加上栈顶元素下标所在的值(这一步是为了消除栈顶元素的影响)
弹出栈顶元素,直到当前元素比栈顶元素大,否则一直执行上述步骤。
然后将区间
[
[
[栈顶元素+1, 当前枚举的右端点
]
]
] 减去当前元素(这一步是为了加上这新元素的影响)
对于
m
a
x
max
max 维护一个下降的单调栈,与上述操作类似,不再赘述。
我们维护完了最大最小值,我们再维护 ( r − l ) × g c d (r-l)\times gcd (r−l)×gcd。我们记 d = ∣ a i − 1 − a i ∣ d = |a_{i-1} - a_i| d=∣ai−1−ai∣,我们更新区间 [ 1 , r − 1 ] [1, r-1] [1,r−1]
当区间的
g
g
g 满足
g
!
=
0
&
&
d
∣
g
g != 0 \&\& \ d|g
g!=0&& d∣g时,说明这个区间新加入这个数是能维持
g
g
g 不变的,所以我们新加入一个数的影响是将这个区间的值减去一个
g
g
g (因为区间长度变长了,所以要减去一个
g
g
g 如果不懂再看看
d
a
t
dat
dat 的定义)
如果不满足上述条件,我们需要将之间的
(
r
−
l
)
∗
g
(r-l) * g
(r−l)∗g加上(消去之前的影响),再将
g
=
g
c
d
(
g
,
d
)
g = gcd(g, d)
g=gcd(g,d),然后减去
(
r
−
l
)
∗
g
(r-l) * g
(r−l)∗g,(加上这个数的影响)
Code
#include <bits/stdc++.h>
#define ll long long
#define qc ios::sync_with_stdio(false); cin.tie(0);cout.tie(0)
#define fi first
#define se second
#define PII pair<int, int>
#define PLL pair<ll, ll>
#define pb push_back
using namespace std;
const int MAXN = 2e5 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1e9 + 7;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;
}
int n;
int stmi[MAXN], stma[MAXN];
ll a[MAXN];
ll gcd(ll a, ll b){
return b ? gcd(b, a%b) : a;
}
struct Tree{
int l, r;
ll dat, g, add, cnt;
}tree[MAXN << 2];
void pushup(int rt){
tree[rt].cnt = 0;
tree[rt].dat = min(tree[rt << 1].dat, tree[rt << 1 | 1].dat);
if(tree[rt].dat == tree[rt << 1].dat)
tree[rt].cnt += tree[rt << 1].cnt;
if(tree[rt].dat == tree[rt << 1 | 1].dat)
tree[rt].cnt += tree[rt << 1 | 1].cnt;
if(tree[rt << 1].g && tree[rt << 1 | 1].g && tree[rt << 1].g == tree[rt << 1 | 1].g)
tree[rt].g = tree[rt << 1].g;
else
tree[rt].g = 0;
}
void build(int l, int r, int rt){
tree[rt].l = l;
tree[rt].r = r;
tree[rt].cnt = tree[rt].add = tree[rt].g = tree[rt].dat = 0ll;
if(l == r){
tree[rt].cnt = 1ll;
return ;
}
int m = (l + r) >> 1;
build(l, m, rt << 1);
build(m+1, r, rt << 1 | 1);
pushup(rt);
}
void pushdown(int rt){
if(tree[rt].add){
tree[rt << 1].dat += tree[rt].add;
tree[rt << 1].add += tree[rt].add;
tree[rt << 1 | 1].dat += tree[rt].add;
tree[rt << 1 | 1].add += tree[rt].add;
tree[rt].add = 0;
}
}
void update(int L, int R, ll val, int rt){
if(L <= tree[rt].l && tree[rt].r <= R){
tree[rt].dat += val ;
tree[rt].add += val;
return;
}
pushdown(rt);
int m = (tree[rt].l + tree[rt].r) >> 1;
if(L <= m)
update(L, R, val, rt << 1);
if(m < R)
update(L, R, val, rt << 1 | 1);
pushup(rt);
}
void updateGcd(int R, int l, int r, ll g, int rt){
if(l <= tree[rt].l && tree[rt].r <= r && tree[rt].g && g % tree[rt].g == 0){
tree[rt].dat -= tree[rt].g;
tree[rt].add -= tree[rt].g;
}
else if (tree[rt].l == tree[rt].r){
tree[rt].dat += (R - tree[rt].l - 1) * tree[rt].g;
tree[rt].add += (R - tree[rt].l - 1) * tree[rt].g;
tree[rt].g = gcd(g, tree[rt].g);
tree[rt].dat -= (R - tree[rt].l) * tree[rt].g;
tree[rt].add -= (R - tree[rt].l) * tree[rt].g;
}
else{
pushdown(rt);
int m = (tree[rt].l + tree[rt].r) >> 1;
if(l <= m)
updateGcd(R, l, r, g, rt << 1);
if(m < r)
updateGcd(R, l, r, g, rt << 1 | 1);
pushup(rt);
}
}
ll query(int L, int R, int rt){
if(L <= tree[rt].l && tree[rt].r <= R){
return tree[rt].dat == 0 ? tree[rt].cnt : 0;
}
pushdown(rt);
int m = (tree[rt].l + tree[rt].r) >> 1;
ll ret = 0;
if(L <= m)
ret += query(L, R, rt << 1);
if(m < R)
ret += query(L, R, rt << 1 | 1);
return ret;
}
void solve(){
scanf("%d",&n);
for(int i = 1; i <= n; i++)
scanf("%lld", a+i);
ll ans = 0;
build(1, n, 1);
int topmi = 0, topma = 0;
for(int i = 1; i <= n; i++){
while(topmi && a[stmi[topmi]] > a[i]){
update(stmi[topmi - 1] + 1, stmi[topmi], a[stmi[topmi]], 1);
topmi--;
}
update(stmi[topmi] + 1, i, -a[i], 1);
stmi[++topmi] = i;
while(topma && a[stma[topma]] < a[i]){
update(stma[topma - 1] + 1, stma[topma], -a[stma[topma]], 1);
topma--;
}
update(stma[topma] + 1, i, a[i], 1);
stma[++topma] = i;
if(i != 1){
updateGcd(i, 1, i-1, abs(a[i] - a[i-1]), 1);
}
ans += query(1, i, 1);
}
printf("%lld\n",ans);
}
int main()
{
#ifdef ONLINE_JUDGE
#else
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
// qc;
int T;
// cin >> T;
scanf("%d",&T);
// T = 1;
while(T--){
solve();
}
return 0;
}