BZOJ 4237稻草人【分治】

来源:互联网 发布:网络的分层结构 编辑:程序博客网 时间:2024/06/11 14:50

Description

JOI村有一片荒地,上面竖着N个稻草人,村民们每年多次在稻草人们的周围举行祭典。

有一次,JOI村的村长听到了稻草人们的启示,计划在荒地中开垦一片田地。和启示中的一样,田地需要满足以下条件:

田地的形状是边平行于坐标轴的长方形;

左下角和右上角各有一个稻草人;

田地的内部(不包括边界)没有稻草人。

给出每个稻草人的坐标,请你求出有多少遵从启示的田地的个数

题解

分治。由于是两维的,可以使其中一维有序来降低思考难度。

先按x从大到小排序,考虑将元素分成左右两半,考虑如何合并这两半的答案,考虑将两个区间分别按照y排序,枚举左下角的点。枚举一个左下角的端点,在右上角肯定要比它高,那就把所有y值比它大的点都加入到一个栈中,维护单调栈,显然,这一定是一个x做坐标单调升的栈。对于右边的点,也做类似的处理,可以发现,左边是一个单调降的栈,而对于每个左下角的点,它的右上角的y值不能大于它在栈中的前一个点的y值,而因为y是有序的,所以直接在右边的栈中二分就可以了。

时间复杂度:O(nlog2n)

代码

#include<cstdio>#include<cstring>#include<algorithm>#define maxn 200006#define LL long longusing namespace std;inline char nc(){    static char buf[100000],*i=buf,*j=buf;    return i==j&&(j=(i=buf)+fread(buf,1,100000,stdin),i==j)?EOF:*i++;}inline int _read(){    char ch=nc();int sum=0;    while(!(ch>='0'&&ch<='9'))ch=nc();    while(ch>='0'&&ch<='9')sum=sum*10+ch-48,ch=nc();    return sum;}struct data{    int x,y;    bool operator <(const data&b)const{return x<b.x;}}a[maxn],stack[2][maxn];int n,top[2];LL ans;int find(int x){    int l=1,r=top[1];    while(l<=r){        int mid=(l+r)>>1;        if(stack[1][mid].y>x)l=mid+1;                        else r=mid-1;    }    return l;}bool cmp(data x,data y){return x.y>y.y;}void work(int l,int r){    if(l>=r)return;    int mid=(l+r)>>1;    work(l,mid);work(mid+1,r);    top[0]=top[1]=0;int j=mid+1;    for(int i=l;i<=mid;i++){        while(j<=r&&a[j].y>a[i].y){            while(top[1]&&stack[1][top[1]].x>a[j].x)top[1]--;            stack[1][++top[1]]=a[j++];        }        while(top[0]&&stack[0][top[0]].x<a[i].x)top[0]--;        stack[0][++top[0]]=a[i];        if(top[0]>1)ans+=top[1]-find(stack[0][top[0]-1].y)+1;               else ans+=top[1];    }    sort(a+l,a+1+r,cmp);}int main(){    freopen("strawman.in","r",stdin);    freopen("strawman.out","w",stdout);    n=_read();    for(int i=1;i<=n;i++)a[i].x=_read(),a[i].y=_read();    sort(a+1,a+1+n);    work(1,n);    printf("%lld\n",ans);    return 0;}
原创粉丝点击