Luogu P10144 [WC/CTS2024] 水镜 题解
对于任何一段连续上升的区间,我们不需要管它。对于任何一段连续下降的区间,我们只需要用 \(2L\) 减去每个数就可以化为一段连续上升的区间。因此,对于这两种区间,我们可以看作一个点。
于是,我们发现其实 \(2L\) 只会被波峰 \(a_{i}\ge a_{i-1},a_{i}\ge a_{i+1}\) 和波谷 \(a_{i}\le a_{i-1},a_{i}\le a_{i+1}\) 限制住。我们考虑对于每个波峰和波谷,是否存在一种方式可以走过去。
对于一个波峰 \(a_{i}\ge a_{i-1},a_{i}\ge a_{i+1}\),我们有两种方法:
第一种,\(a_{i+1}\to2L-a_{i+1}\),这样就得满足 \(2L-a_{i+1}\gt a_{i}\),即 \(2L\gt a_{i}+a_{i+1}\)。
第二种,\(a_{i}\to2L-a_{i}\) 且 \(a_{i+1}\to2L-a_{i+1}\),这种情况下显然 \(a_{i+1}\gt a_i\)。但是这样就得满足 \(2L-a_{i}\gt a_{i-1}\),即 \(2L\gt a_{i}+a_{i-1}\)。
如果 \(2L\gt a_{i}+\min(a_{i-1},a_{i+1})\),则一定存在上述两种方法中的一种可以走过这个波峰。
对于一个波谷 \(a_{i}\le a_{i-1},a_{i}\le a_{i+1}\),我们有两种方法:
第一种,\(a_{i-1}\to2L-a_{i-1}\) 且 \(a_{i}\to2L-a_{i}\),这种情况下显然 \(a_{i}\gt a_{i-1}\)。这样就得满足 \(2L-a_{i}\lt a_{i+1}\),即 \(2L\lt a_{i}+a_{i+1}\)。
第二种,\(a_{i}\to2L-a_{i}\),这样就得满足 \(2L-a_{i}\lt a_{i-1}\),即 \(2L\lt a_{i}+a_{i-1}\)。这里不需要管对 \(a_{i+1}\) 的影响,因为在下一个波峰中会统计。
如果 \(2L\lt a_{i}+\max(a_{i-1},a_{i+1})\),则一定存在上述两种方法中的一种可以走过这个波谷。
对于波峰套波峰或波峰套波谷一类的情况,这样也是对的。原因是这样的限制给了每个波峰和波谷两种选择方式,套起来的两个波峰或波谷一定有一种方式可以匹配。
最后,我们发现合法的区间可以使用双指针维护。上面的这些限制可以使用 multiset 来维护,支持插入,删除,查询最值。
时间复杂度 \(O(n\log n)\),非常优秀。
#include <bits/stdc++.h>
using namespace std;
long long n,a[600000],ans=0,j=3;
multiset<long long>b,s;
void add(long long x)
{
if(x==n)return;
if(a[x]>=a[x-1]&&a[x]>=a[x+1])s.insert(-a[x]-min(a[x-1],a[x+1]));
if(a[x]<=a[x-1]&&a[x]<=a[x+1])b.insert(a[x]+max(a[x-1],a[x+1]));
}
void del(long long x)
{
if(x==n)return;
if(a[x]>=a[x-1]&&a[x]>=a[x+1])s.erase(s.find(-a[x]-min(a[x-1],a[x+1])));
if(a[x]<=a[x-1]&&a[x]<=a[x+1])b.erase(b.find(a[x]+max(a[x-1],a[x+1])));
}
int main()
{
scanf("%lld",&n);
for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
add(2);
for(int i=1;i<=n;i++)
{
if(i!=1)del(i);
while((b.empty()||s.empty()||*b.begin()>-(*s.begin()))&&j<=n)add(j),j++;
ans+=(j-i);
}
printf("%lld\n",ans-n);
return 0;
}

浙公网安备 33010602011771号