题目大意
一个n×m的矩形网格区域,有n个关键点在上面。这些点的
求这个网格区域存在多少个平行于坐标轴的矩形满足:
∙矩形的内部(不包括边界)没有任何关键点。
1≤n≤2×105,0≤xi,yi≤109
题目分析
先考虑对于一个关键点而言,我们怎么求出它作为左下角时所有的合法矩形。显然我们可以计算出其右上方所有关键点x递增
那么现在我们有很多个关键点都要计算,怎么办呢?考虑使用分治。我们按照x坐标分治,每次计算所有跨过
怎么计算这个跨中线矩形数量呢?我们将所有在当前分治范围内的点按照y坐标从大到小排序,然后依次插入关键点。可以发现,我们对于所有
当然,为了让程序跑得更快,我们在分治过程中的排序可以使用归并排序,这样的话我们就要先分治再计算当前区间答案。
时间复杂度
代码实现
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cctype>
using namespace std;
inline int read()
{
int x=0,f=1;
char ch=getchar();
while (!isdigit(ch)) f=ch=='-'?-1:f,ch=getchar();
while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();
return x*f;
}
const int X=1000000000;
const int N=200050;
struct P
{
int x,y;
P(int x_=0,int y_=0){x=x_,y=y_;}
}scs[N];
bool cmp1(P a,P b){return a.x<b.x;}
bool cmp2(P a,P b){return a.y>b.y;}
long long ans;
P stack[2][N];
int top[2];
P tmp[N];
int n;
void divide(int l,int r)
{
if (l==r) return;
int mid=l+r>>1,x0=scs[mid].x;
divide(l,mid),divide(mid+1,r);
int cnt=0,cur1=l,cur2=mid+1;
for (;cur1<=mid||cur2<=r;)
if (cur2>r||cur1<=mid&&cmp2(scs[cur1],scs[cur2])) tmp[++cnt]=scs[cur1++];
else tmp[++cnt]=scs[cur2++];
memcpy(scs+l,tmp+1,cnt*sizeof(P));
stack[0][top[0]=0]=P(X+1,X+1),stack[1][top[1]=0]=P(-X-1,X+2);
for (int i=l;i<=r;i++)
if (scs[i].x<=x0)
{
for (;top[0]&&stack[0][top[0]].x<scs[i].x;top[0]--);
int y0=stack[0][top[0]].y;
stack[0][++top[0]]=scs[i];
int L=0,R=top[1],Mid,Ret=-1;
while (L<=R)
{
Mid=L+R>>1;
if (stack[1][Mid].y>y0) L=(Ret=Mid)+1;
else R=Mid-1;
}
ans+=top[1]-Ret;
}
else
{
for (;top[1]&&stack[1][top[1]].x>scs[i].x;top[1]--);
stack[1][++top[1]]=scs[i];
}
}
int main()
{
freopen("scarecrows.in","r",stdin),freopen("scarecrows.out","w",stdout);
n=read();
for (int i=1;i<=n;i++) scs[i].x=read(),scs[i].y=read();
sort(scs+1,scs+1+n,cmp1);
ans=0,divide(1,n);
printf("%lld\n",ans);
fclose(stdin),fclose(stdout);
return 0;
}