Luogu P10144 [WC/CTS2024] 水镜 题解

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;
}
posted @ 2025-02-15 15:44  w9095  阅读(34)  评论(0)    收藏  举报