CF1156E Special Segments of Permutation
一般有在序列上进行与最大值相关的操作都是单调栈或者笛卡尔树来操作,但其实分治做法也是可以做的
至于分治做法为啥不会成为主流,可能是因为大多数这种序列题用单调栈或者笛卡尔树可以\(O(n)\)做,
而分治只能是满的\(O(nlog(n))\),少数部分用笛卡尔树的启发式合并是\(O(nlog(n))\)的
首先当我们处理区间\([l,r]\)的时候,最大值显然不在\([l,mid]\)就在\([mid+1,r]\)
强制让最大值在其中左侧,我们发现从\(mid\)循环到\(l\)的时候最大值是单调不降的
那么可以让另一侧用单调指针扫过,让右侧的数字小于左侧的最大值,然后每个左端点统计一次答案
然后在强制让最大值在右侧做一次,注意一下处理左侧和右侧要一个是开区间,一个是闭区间,不可以全闭\(or\)全开
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int maxn=2e5+5;
int a[maxn],n,ans;
int tong[maxn];
inline void msort(int l,int r)
{
if(l==r) return;
int mid=(l+r)>>1;
msort(l,mid);msort(mid+1,r);
int tmp2=mid,maxx=0;
for(int i=mid;i>=l;i--)
{
maxx=max(a[i],maxx);
while(a[tmp2+1]<maxx&&tmp2<r){tmp2++;tong[a[tmp2]]++;}
ans+=tong[maxx-a[i]];
}
for(int i=mid+1;i<=r;i++) tong[a[i]]=0;
int tmp1=mid+1;maxx=0;
for(int i=mid+1;i<=r;i++)
{
maxx=max(a[i],maxx);
while(a[tmp1-1]<maxx&&tmp1>l){tmp1--;tong[a[tmp1]]++;}
ans+=tong[maxx-a[i]];
}
for(int i=l;i<=mid;i++) tong[a[i]]=0;
}
signed main()
{
n=read();for(int i=1;i<=n;i++)a[i]=read();
msort(1,n);printf("%d\n",ans);
}