CF526F Pudding Monsters

Pudding Monsters

题意

给定一个 \(1\)\(n\) 的排列 \(p\),求其排序后连续的子区间个数。

\(1 \leq n \leq 3 \times 10^5\)

思路

子区间 \([l,r]\) 排序后连续等价于 \(\max\limits_{i=l}^{r}p_i-\min\limits_{i=l}^{r}p_i=r-l\)

考虑分治。假设当前处理到区间 \([L,R]\),我们只需要考虑跨越中线的子区间 \([l,r]\) 即可。发现满足条件的子区间 \([l,r]\) 可分为两大类。

如果最大值和最小值在同侧,不妨令其均在左侧。枚举所有满足 \(l \in [L,mid]\) 的左端点 \(l\),则有 \(r=\max\limits_{i=l}^{mid}p_i-\min\limits_{i=l}^{mid}p_i+l\),此时检查合法性即可,即:

  • \(r \in [mid+1,R]\)

  • \(\max\limits_{i=l}^{mid}p_i > \max\limits_{i=mid+1}^{r}p_i\)

  • \(\min\limits_{i=l}^{mid}p_i < \min\limits_{i=mid+1}^{r}p_i\)

如果最大值和最小值在异侧,不妨令最大值在左侧,最小值在右侧。考虑 \(\max\limits_{i=l}^{mid}p_i+l=\min\limits_{i=mid+1}^{r}p_i+r\)。如果我们已经固定 \(l\),此时要满足:

  • \(\max\limits_{i=l}^{mid}p_i > \max\limits_{i=mid+1}^{r}p_i\)

  • \(\min\limits_{i=l}^{mid}p_i > \min\limits_{i=mid+1}^{r}p_i\)

容易发现,依据这种大小关系,枚举 \(l\)\(r\) 的候选区间是可以双指针的。

当最大值在右侧,最小值在左侧时,\(\min\limits_{i=l}^{mid}p_i-l=\max\limits_{i=mid+1}^{r}p_i-r\),要满足:

  • \(\max\limits_{i=l}^{mid}p_i < \max\limits_{i=mid+1}^{r}p_i\)

  • \(\min\limits_{i=l}^{mid}p_i < \min\limits_{i=mid+1}^{r}p_i\)

枚举 \(r\),此时 \(l\) 也可以双指针。

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

代码

#include<iostream>
#include<cstdio>
using namespace std;
long long ans;
int a[300010],maxn[300010],minn[300010],cnt[1000010];
void solve(int L,int R){
	if(L==R){
		ans++;
		return ;
	}
	int lmid=(L+R)>>1,rmid=lmid+1,tmpl,tmpr;
	solve(L,lmid);
	solve(rmid,R);
	maxn[lmid]=minn[lmid]=a[lmid];
	for(int i=lmid-1;i>=L;i--){
		maxn[i]=max(maxn[i+1],a[i]);
		minn[i]=min(minn[i+1],a[i]);
	}
	maxn[rmid]=minn[rmid]=a[rmid];
	for(int i=rmid+1;i<=R;i++){
		maxn[i]=max(maxn[i-1],a[i]);
		minn[i]=min(minn[i-1],a[i]);
	}
	for(int l=lmid;l>=L;l--){
		int r=maxn[l]-minn[l]+l;
		if(r>=rmid  &&  r<=R  &&  maxn[l]>maxn[r]  &&  minn[l]<minn[r]){
			ans++; 
		}
	}
	for(int r=rmid;r<=R;r++){
		int l=minn[r]-maxn[r]+r;
		if(l>=L  &&  l<=lmid  &&  maxn[l]<maxn[r]  &&  minn[l]>minn[r]){
			ans++;
		}
	}
	tmpl=rmid,tmpr=rmid-1;
	for(int l=lmid;l>=L;l--){
		while(tmpr+1<=R  &&  maxn[tmpr+1]<maxn[l]){
			tmpr++;
			cnt[minn[tmpr]+tmpr]++;
		}
		while(tmpl<=tmpr  &&  minn[tmpl]>minn[l]){
			cnt[minn[tmpl]+tmpl]--;
			tmpl++;
		}
		ans+=cnt[maxn[l]+l];
	}
	while(tmpl<=tmpr){
		cnt[minn[tmpl]+tmpl]--;
		tmpl++;
	}
	tmpl=lmid+1,tmpr=lmid;
	for(int r=rmid;r<=R;r++){
		while(tmpl-1>=L  &&  maxn[tmpl-1]<maxn[r]){
			tmpl--;
			cnt[minn[tmpl]-tmpl]++;
		}
		while(tmpr>=tmpl  &&  minn[tmpr]>minn[r]){
			cnt[minn[tmpr]-tmpr]--;
			tmpr--;
		}
		ans+=cnt[maxn[r]-r];
	}
	while(tmpr>=tmpl){
		cnt[minn[tmpr]-tmpr]--;
		tmpr--;
	}
}
int main(){
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		int x,y;
		scanf("%d %d",&x,&y);
		a[x]=y+n;
	}
	solve(1,n);
	printf("%lld",ans);
	return 0;
}
posted @ 2025-06-13 20:16  Oken喵~  阅读(1)  评论(0)    收藏  举报