[BZOJ4237]稻草人(cdq分治+单调栈+二分)

来源:互联网 发布:sql进阶书籍 编辑:程序博客网 时间:2024/05/29 02:30

题目描述

传送门

题解

对x排序了之后按照x分治,每一次对y排序
考虑如何处理左区间里的点对右边的点的影响,也就是如何计算左边和右边配对的情况
用两个指针扫的时候,如果左边的连续一段区间里的点想要都和右边的某一个点配对的话,必须满足x单调递减
而右边的区间的某一个点如果要是想和左边的点配对的话,只能是y在 它和第一个x在它左边的点 所确定的y的范围内的点
对于左边的点维护一个x单调递减的栈,对右边的点维护一个x单调递增的栈,然后对于每一个右边的点,在左边查询栈中y在它和它前一个点的y的范围内的点
画个图理解一下…

代码

#include<algorithm>#include<iostream>#include<cstring>#include<cstdio>#include<cmath>using namespace std;#define N 200005#define LL long longint n,topl,topr,sl[N],sr[N];struct data{int x,y;}q[N],p[N];LL ans;int cmp(data a,data b){    return a.x<b.x;}void pushl(int id){    while (topl&&q[id].x>q[sl[topl]].x)        --topl;    sl[++topl]=id;}void pushr(int id){    while (topr&&q[id].x<q[sr[topr]].x)        --topr;    sr[++topr]=id;}int find(int id){    if (!id) return topl;    int l=1,r=topl,mid,ans=0;    while (l<=r)    {        mid=(l+r)>>1;        if (q[sl[mid]].y>q[id].y) ans=topl-mid+1,r=mid-1;        else l=mid+1;    }    return ans;}void cdq(int l,int r){    if (l>=r) return;    int mid=(l+r)>>1;    cdq(l,mid);    cdq(mid+1,r);    topl=topr=0;    int pl=l,pr=mid+1;    while (pr<=r)    {        pushr(pr);        while (pl<=mid&&q[pl].y<q[pr].y)        {            pushl(pl);            ++pl;        }        ans+=(LL)find(sr[topr-1]);        ++pr;    }    int ll=l,rr=mid+1,now=l;    while (ll<=mid&&rr<=r)    {        if (q[ll].y<q[rr].y) p[now++]=q[ll++];        else p[now++]=q[rr++];    }    while (ll<=mid) p[now++]=q[ll++];    while (rr<=r) p[now++]=q[rr++];    for (int i=l;i<=r;++i) q[i]=p[i];}int main(){    scanf("%d",&n);    for (int i=1;i<=n;++i) scanf("%d%d",&q[i].x,&q[i].y);    sort(q+1,q+n+1,cmp);    cdq(1,n);    printf("%lld\n",ans);}
0 0
原创粉丝点击