- 一个(n imes n)的正方形,其中每行每列恰有一个点。
- 问有多少子正方形,也满足每行每列恰有一个点。
- (nle3 imes10^5)
二维化一维
显然这道题很容易想到降维。
既然每行只有一个点,我们可以用(a_i)表示第(i)行的点在第几列。
现在问题就变成有多少区间满足其中元素排序之后值连续。
则区间([l,r])合法的充要条件就是(max_{i=l}^ra_i-min_{i=l}^ra_i=r-l)。
分治套路题
对于当前分治区间([l,r]),设其中点为(mid),那么([l,mid])和([mid+1,r])两区间各自的答案可以直接递归处理。
接着我们先预处理出(Mx,Mn)两个数组,当(iin[l,mid])时表示([i,mid])的最值,当(iin[mid+1,r])时表示([mid+1,i])的最值。
现在我们就要考虑如何统计跨中点的区间的答案,分成两类情况。
两最值在中点同侧
假设在([l,mid])中。
我们枚举左端点(i),此时最值为(Mx_i,Mn_i),所以满足(Mx_i-Mn_i=j-i),即(j=Mx_i-Mn_i+i)。
那么我们只要验证一下(j)是不是一个合法的右端点即可。
两最值在中点异侧
假设最小值在([l,mid])中,最大值在([mid+1,r])中。
我们从大到小枚举(i),同时开(p,q)两个指针维护出区间([p,q])表示合法右端点(j)的取值区间,显然这东西随着(i)的移动是单调右移的。
此时最值为(Mx_j,Mn_i),故(Mx_j-Mn_i=j-i),即(Mn_i-i=Mx_j-j)。
那么只要开桶记一下(Mx_j-j=x)的(j)的个数,就可以了。
代码:(O(nlogn))
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 300000
#define LL long long
using namespace std;
int n,a[N+5];LL ans;
int Mx[N+5],Mn[N+5],c[2*N+5];I void Solve(CI l,CI r)//分治
{
if(l==r) return (void)++ans;RI i,p,q,mid=l+r>>1;Solve(l,mid),Solve(mid+1,r);//递归子区间
Mx[mid]=Mn[mid]=a[mid],Mx[mid+1]=Mn[mid+1]=a[mid+1];
for(i=mid-1;i>=l;--i) Mx[i]=max(Mx[i+1],a[i]),Mn[i]=min(Mn[i+1],a[i]);//预处理左区间后缀最值
for(i=mid+2;i<=r;++i) Mx[i]=max(Mx[i-1],a[i]),Mn[i]=min(Mn[i-1],a[i]);//预处理右区间前缀最值
for(i=l;i<=mid;++i) p=i+Mx[i]-Mn[i],mid<p&&p<=r&&Mn[i]<Mn[p]&&Mx[p]<Mx[i]&&++ans;//最值都在左区间
for(i=mid+1;i<=r;++i) p=i-Mx[i]+Mn[i],l<=p&&p<=mid&&Mn[i]<Mn[p]&&Mx[p]<Mx[i]&&++ans;//最值都在右区间
for(i=mid,p=q=mid+1;i>=l;ans+=c[Mn[i]-i+n],--i)//最小值在左区间,最大值在右区间
{W(q<=r&&Mn[q]>Mn[i]) ++c[Mx[q]-q+n],++q;W(p^q&&Mx[p]<Mx[i]) --c[Mx[p]-p+n],++p;}//p,q维护合法右端点区间
W(p^q) --c[Mx[p]-p+n],++p;//清空
for(i=mid,p=q=mid+1;i>=l;ans+=c[Mx[i]+i],--i)//最大值在左区间,最小值在右区间
{W(q<=r&&Mx[q]<Mx[i]) ++c[Mn[q]+q],++q;W(p^q&&Mn[p]>Mn[i]) --c[Mn[p]+p],++p;}
W(p^q) --c[Mn[p]+p],++p;//清空
}
int main()
{
RI i,x,y;for(scanf("%d",&n),i=1;i<=n;++i) scanf("%d%d",&x,&y),a[x]=y;//二维化一维
return Solve(1,n),printf("%lld
",ans),0;
}