链接
https://codeforces.com/problemset/problem/1156/E
题解
这题想到了就没啥难的了
我显然可以枚举最大的那个数是谁,然后确定出在哪个范围内它是最大的(求出左右第一个比他大的分别在哪,基础单调栈)
假设我求出来之后是
[
l
i
,
r
i
]
[l_i,r_i]
[li,ri]
现在问题就是我怎么枚举,肯定左端就在
[
l
i
,
i
)
[l_i,i)
[li,i),右端点就在
(
i
,
r
i
]
(i,r_i]
(i,ri]
考虑我如果选择较短的那段,那么最差的情况就是
r
i
−
l
i
+
1
2
\frac{r_i-l_i+1}{2}
2ri−li+1这么多循环次数
再想一想,一种极端情况就是序列递增,那么我如果从大到小依次的话,虽然每次区间都很长,但是由于我取较短的那段,每次都是
O
(
0
)
O(0)
O(0)的,因此没问题
如果序列长得线段树那样,每次切一半,那我依然可以做到
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn),这其实就似乎最差情况了,想一下,如果稍微不平衡一点,那么根据我取最小的策略,我每次枚举的次数就会减少一些
所以这样做复杂度是比较优秀的
代码
#include <bits/stdc++.h>
#define maxn 200010
#define linf (1ll<<60)
using namespace std;
typedef long long ll;
ll a[maxn], N, l[maxn], r[maxn], f[maxn];
stack<ll> stk;
ll read(ll x=0)
{
ll c, f=1;
for(c=getchar();!isdigit(c);c=getchar())if(c=='-')f=-f;
for(;isdigit(c);c=getchar())x=x*10+c-48;
return f*x;
}
int main()
{
ll N=read(), i, ans(0), j;
a[0]=a[N+1]=linf;
for(i=1;i<=N;i++)a[i]=read(), f[a[i]]=i;
stk.emplace(0);
for(i=1;i<=N;i++)
{
while(a[stk.top()]<a[i])stk.pop();
l[i]=stk.top()+1;
stk.emplace(i);
}
while(!stk.empty())stk.pop();
stk.emplace(N+1);
for(i=N;i;i--)
{
while(a[stk.top()]<a[i])stk.pop();
r[i]=stk.top()-1;
stk.emplace(i);
}
for(i=1;i<=N;i++)
{
if(i-l[i] < r[i]-i)
{
for(j=l[i];j<i;j++)
{
if(i<f[a[i]-a[j]] and f[a[i]-a[j]]<=r[i])ans++;
}
}
else
{
for(j=i+1;j<=r[i];j++)
{
if(l[i]<=f[a[i]-a[j]] and f[a[i]-a[j]]<i)ans++;
}
}
}
printf("%lld",ans);
return 0;
}