###Description
经过侦查,你绘制出了魔族大本营的地图,然后发现,魔族大本营是一个N×N的网格图,一共有N支军队驻扎在一些网格中(不会有两只军队驻扎在一起)。
在大本营中,每有一个k×k(1≤k≤N)的子网格图包含恰好k支军队,我们袭击的难度就会增加1点。
现在请你根据绘制出的地图,告诉爱丽丝这次的袭击行动难度有多大。
输入保证每一行和每一列都恰有一只军队。
###Data Constraint
对于30%的数据,N ≤ 100
对于60%的数据,N ≤ 5000
对于100%的数据,N ≤ 50000
###Solution
对于30%的数据,暴力找即可。
那对于60+%的数据呢?
转化一下题目,我们把军队的x坐标看成下标,y坐标看成下标为x的权值(即ax=ya_x=yax=y),题目就变成了:问有多少个区间[L,R][L,R][L,R]满足,它们中间的数是连续的一段。
进而转化为了:有多少个区间[L,R][L,R][L,R]满足,max(aL,⋯ ,aR)−min(aL,⋯ ,aR)=r−lmax(a_L,\cdots,a_R)-min(a_L,\cdots,a_R)=r-lmax(aL,⋯,aR)−min(aL,⋯,aR)=r−l。
那么n2n^2n2算法就出来了。
能不能更快一些呢?我们考虑分治。
对于一段区间[L,R][L,R][L,R],它的答案等于区间[L,Mid][L,Mid][L,Mid]的答案加上区间[Mid+1,R][Mid+1,R][Mid+1,R]的答案再加上跨越中间的答案(这里MidMidMid表示区间[L,R][L,R][L,R]的中点)。
我们先预处理出每个位置到MidMidMid的最小值,最大值。
(接下来的max(l,r)max(l,r)max(l,r),min(l,r)min(l,r)min(l,r)表示区间[l,r][l,r][l,r]的最大值,最小值)
对于跨越中间的答案,它的最值分布有四种情况:
####最大值和最小值都在左侧
我们先枚举一个lll,表示这个合法区间的左端点。如果最值都在左侧,那么根据题意,右端点r=l+max(l,Mid)−min(l,Mid)r=l+max(l,Mid)-min(l,Mid)r=l+max(l,Mid)−min(l,Mid)。右端点rrr得出后,如何判断它的合法性?
如上图,首先r′r'r′端点肯定是不合法的(因为它没有跨越MidMidMid)。
然后我们看看rrr端点。我们首先确定了最值都在左侧,那么这个rrr端点必须满足:
max(Mid+1,r)<max(l,Mid)max(Mid+1,r)<max(l,Mid)max(Mid+1,r)<max(l,Mid)
min(Mid+1,r)>min(l,Mid)min(Mid+1,r)>min(l,Mid)min(Mid+1,r)>min(l,Mid)
####最大值最小值都在右侧
这种情况与以上的情况类似,只不过是枚举右端点,算出左端点,然后判断合法性。
####最大值在右侧,最小值在左侧
首先还是枚举左端点lll,我们知道,max(Mid+1,i)(i>Mid)max(Mid+1,i)(i>Mid)max(Mid+1,i)(i>Mid)随着iii的增大是单调不下降的,min(Mid+1,i)(i>Mid)min(Mid+1,i)(i>Mid)min(Mid+1,i)(i>Mid)也同理。
于是我们新建两个指针r1r1r1,r2r2r2。
r2r2r2要使min(Mid+1,r2)>min(l,Mid)min(Mid+1,r2)>min(l,Mid)min(Mid+1,r2)>min(l,Mid),这样才满足最小值在左侧的情况。
r1r1r1要使max(Mid+1,r1−1)<max(l,Mid)max(Mid+1,r1-1)<max(l,Mid)max(Mid+1,r1−1)<max(l,Mid),即区间[Mid+1,r1−1][Mid+1,r1-1][Mid+1,r1−1]是不合法的情况。
对于每一个左端点lll,指针移动后,r1r1r1到r2r2r2之间的所有点都可以是合法的右端点。
那么怎么统计右端点个数呢?
我们知道对于一个合法的区间它满足max(l,r)−min(l,r)=r−lmax(l,r)-min(l,r)=r-lmax(l,r)−min(l,r)=r−l。
把原式移项得:max(l,r)−r=min(l,r)−lmax(l,r)-r=min(l,r)-lmax(l,r)−r=min(l,r)−l。
对于这种情况就是:max(Mid+1,r)−r=min(l,Mid)−lmax(Mid+1,r)-r=min(l,Mid)-lmax(Mid+1,r)−r=min(l,Mid)−l。
于是,对于移动的r2r2r2,我们把max(Mid+1,r2)−r2max(Mid+1,r2)-r2max(Mid+1,r2)−r2丢进桶里,对于移动前的r1r1r1,我们再把max(Mid+1,r1)−r1max(Mid+1,r1)-r1max(Mid+1,r1)−r1从桶里扔出来。
每次指针移动后,若r1<=r2r1<=r2r1<=r2则把答案加上min(l,Mid)−lmin(l,Mid)-lmin(l,Mid)−l所在桶里的个数。
注意每次桶都要清空。
####最大值在左侧,最小值在右侧
其实与以上方法是一样的,只是枚举的是右端点。
注意:区间满足条件与上稍有不同。
max(l,Mid)−min(Mid+1,r)=r−lmax(l,Mid)-min(Mid+1,r)=r-lmax(l,Mid)−min(Mid+1,r)=r−l
移项得:max(l,Mid)+l=min(Mid+1,r)+rmax(l,Mid)+l=min(Mid+1,r)+rmax(l,Mid)+l=min(Mid+1,r)+r。
所以应该是将max(l2,Mid)+l2max(l2,Mid)+l2max(l2,Mid)+l2丢进桶里,max(l1,Mid)+l1max(l1,Mid)+l1max(l1,Mid)+l1扔出来。
好,这样我们得到的实现复杂度是O(nlog2n)O(nlog_2n)O(nlog2n)的。
###Code
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
#define N 300001
#define ll long long
#define P 600000
using namespace std;
int bz[P*2+1];
int a[N];
int maxl[N],maxr[N],minl[N],minr[N];
ll fz(int l,int r)
{
if(l==r) return 1;
int mid=(l+r)/2;
maxl[mid]=minl[mid]=a[mid];
fd(i,mid-1,l)
{
maxl[i]=max(maxl[i+1],a[i]);
minl[i]=min(minl[i+1],a[i]);
}
maxr[mid+1]=minr[mid+1]=a[mid+1];
ll t=0;
fo(i,mid+2,r)
{
maxr[i]=max(maxr[i-1],a[i]);
minr[i]=min(minr[i-1],a[i]);
}
//minmax|
fd(i,mid,l)
{
int p=i+maxl[i]-minl[i];
if(p<=mid || p>r) continue;
if(minr[p]>minl[i] && maxr[p]<maxl[i]) t++;
}
//|minmax
fo(i,mid+1,r)
{
int p=i-maxr[i]+minr[i];
if(p>mid || p<l) continue;
if(minl[p]>minr[i] && maxl[p]<maxr[i]) t++;
}
//min|max
int z1=mid+1,z2=mid;
fd(i,mid,l)
{
while(minr[z2+1]>minl[i] && z2<r)
{
z2++;
bz[maxr[z2]-z2+P]++;
}
while(maxl[i]>maxr[z1])
{
bz[maxr[z1]-z1+P]--;
z1++;
if(z1>r) break;
}
if(z1>r) break;
if(z1<=z2) t+=bz[minl[i]-i+P];
}
fd(i,mid,l) bz[minl[i]-i+P]=0;
fo(i,mid+1,r) bz[maxr[i]-i+P]=0;
//max|min
z1=mid,z2=mid+1;
fo(i,mid+1,r)
{
while(minl[z2-1]>minr[i] && z2>l)
{
z2--;
bz[maxl[z2]+z2+P]++;
}
while(maxr[i]>maxl[z1])
{
bz[maxl[z1]+z1+P]--;
z1--;
if(z1<l) break;
}
if(z1<l) break;
if(z2<=z1) t+=bz[minr[i]+i+P];
}
fo(i,mid+1,r) bz[minr[i]+i+P]=0;
fd(i,mid,l) bz[maxl[i]+i+P]=0;
return t+fz(l,mid)+fz(mid+1,r);
}
int main()
{
int n;
cin>>n;
fo(i,1,n)
{
int x,y;
scanf("%d %d",&x,&y);
a[x]=y;
}
cout<<fz(1,n);
}