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

题目描述

传送门

题解

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

代码

#include
#include
#include
#include
#include
using namespace std;
#define N 200005
#define LL long long

int 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.xx;
}
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);
}

你可能感兴趣的:(题解,单调栈,二分,cdq分治/整体二分)