[题解]CF875D High Cry

CF875D High Cry ~ Codeforces

我们令:

  • \(L[i]\) 为最小的 \(j\le i\) 使得 \(\max a[j\sim (i-1)]<a[i]\)
  • \(R[i]\) 为最大的 \(j\ge i\) 使得 \(\max a[(i+1)\sim j]\le a[i]\)

可以用单调栈在 \(O(n)\) 内求出。

这样我们就大致刻画出了以 \(a_i\) 为最大值的区间 \([l,r]\) 需要满足的条件之一:\(l\in[L[i],i],r\in[i,R[i]]\)

注意:如果多个值同时作为一个区间的最大值,我们必须保证答案仅由其中一个进行统计。这也是为什么 \(L,R\) 的定义是一个 \(<\) 一个 \(\le\)(很像 ABC407F):

  • 如果都是 \(<\),那么此贡献会被忽略;
  • 如果都是 \(\le\),那么此贡献会在每个最大值处都统计一次,造成重复。

接下来,我们仅需限制 \([l,r]\) 的区间或 \(> \max a[l\sim r]\)

显然固定 \(l\) 不动时,区间或随 \(r\) 不降;固定 \(r\) 不动时,区间或随 \(l\) 不增。

换句话说:

  • 我们一定可以找到一个最小的 \(l'\ge L\),使得 \([l',i]\) 的区间或 \(\le \max\)
  • 我们一定可以找到一个最大的 \(r'\le R\),使得 \([i,r']\) 的区间或 \(\le \max\)

可以用 ST 表 + 二分或倍增 在 \(O(\log n)\) 内求出。

\([L,R]\) 所有包含 \(i\) 的子区间中,是 \([l',r']\) 子区间的,都有区间或 \(=\max\);不是 \([l',r']\) 子区间的,都有区间或 \(>\max\)

后者又可以进一步讨论:

  • \(l\in [L,l'),r\in [i,r']\)。对答案贡献为 \((l'-L)\times(r'-i+1)\)
  • \(l\in [l',i],r\in (r',R]\)。对答案贡献为 \((i-l'+1)\times(R-r')\)
  • \(l\in [L,l'),r\in (r',R]\)。对答案贡献为 \((l'-L)\times(R-r')\)

三种情况累加起来就可以了。

时间复杂度 \(O(n\log n)\)

代码实现求 \(l',r'\) 用了线段树,所以时间复杂度是 \(O(n\log^2 n)\)

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define lc (x<<1)
#define rc (x<<1|1)
using namespace std;
const int N=2e5+10;
int n,a[N],lef[N],rig[N],st[N],top,ans;
struct SEG{
	int sum[N<<2];
	void build(int x,int l,int r){
		if(l==r) return sum[x]=a[l],void();
		int mid=(l+r)>>1;
		build(lc,l,mid),build(rc,mid+1,r);
		sum[x]=sum[lc]|sum[rc];
	}
	int qry(int x,int a,int b,int l,int r){
		if(a>b) return 0;
		if(a<=l&&r<=b) return sum[x];
		int mid=(l+r)>>1,ans=0;
		if(a<=mid) ans|=qry(lc,a,b,l,mid);
		if(b>mid) ans|=qry(rc,a,b,mid+1,r);
		return ans;
	}
}tr;
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	tr.build(1,1,n);
	for(int i=1;i<=n;i++){
		while(top&&a[st[top]]<a[i]) top--;
		lef[i]=top?st[top]+1:1,st[++top]=i;
	}
	top=0;
	for(int i=n;i;i--){
		while(top&&a[st[top]]<=a[i]) top--;
		rig[i]=top?st[top]-1:n,st[++top]=i;
	}
	for(int i=1,pl,pr;i<=n;i++){
		pl=pr=i;
		for(int j=18;~j;j--){
			if(pl-(1<<j)>=lef[i]&&tr.qry(1,pl-(1<<j),i,1,n)==a[i]){
				pl-=(1<<j);
			}
			if(pr+(1<<j)<=rig[i]&&tr.qry(1,i,pr+(1<<j),1,n)==a[i]){
				pr+=(1<<j);
			}
		}
		ans+=(pl-lef[i])*(pr-i+1)+(rig[i]-pr)*(i-pl+1)+(pl-lef[i])*(rig[i]-pr);
	}
	cout<<ans<<"\n";
	return 0;
}
posted @ 2025-08-25 17:41  Sinktank  阅读(14)  评论(0)    收藏  举报
★CLICK FOR MORE INFO★ TOP-BOTTOM-THEME
Enable/Disable Transition
Copyright © 2023 ~ 2025 Sinktank - 1328312655@qq.com
Illustration from 稲葉曇『リレイアウター/Relayouter/中继输出者』,by ぬくぬくにぎりめし.