bzoj4237 稻草人(cdq分治+单调栈+二分)

bzoj4237 稻草人

原题地址:http://www.lydsy.com/JudgeOnline/problem.php?id=4237

题意:
JOI村有一片荒地,JOI村计划在荒地中开垦一片田地,田地需要满足以下条件:
田地的形状是边平行于坐标轴的长方形;
左下角和右上角各有一个稻草人;
田地的内部(不包括边界)没有稻草人。
给出每个稻草人的坐标,请你求出有多少遵从启示的田地的个数

数据范围
1<=N<=2*10^5
0<=Xi<=10^9(1<=i<=N)
0<=Yi<=10^9(1<=i<=N)
Xi(1<=i<=N)互不相同。
Yi(1<=i<=N)互不相同。

题解:
Mercer的题解超详细呀。

最开始想的就是,按x坐标排序后,分治处理跨mid的个数。
首先如果枚举右区间的每个点,首先要考虑同在右区间的点的影响。
那么对于枚举到的这个点来说,要找到前面的第一个y坐标比它小的点。中间的点就没有用了,可以弹掉。这就是一个y递增的单调栈。这个点和它在栈中前一个点形成了一个y的范围。
但是有了这个y的取值范围,到左边却没法统计答案,如果左边直接维护一个y递减的单调栈是不行的,因为我们只能要y小于右边枚举到的那个点的点,否则一个y很大的点进入后弹栈,会弹掉本来有贡献的点。而右边枚举的点的y又不是单调的,也不能两边都同时枚举。
于是要保证另一维的单调性,归并对y排序。
这样,右边维护x递增的单调栈,依然能得到每个点前面的第一个y坐标比它小的点。
对于左边,就可以在右边枚举到一个点时,把y小于它的加进x递减的单调栈。每次二分得到符合要求的点的范围统计答案即可。

代码:

#include
#include
#include
#include
#define LL long long
using namespace std;
const int N=200005;
struct node
{
    int x,y;
}a[N],b[N];
bool cmp(const node &A,const node &B) {return A.xx;}
LL ret=0;
int n,L[N],R[N],lt,rt;
void Add(int i,int opt)
{
    if(opt==0) 
    {
        while(lt&&a[L[lt]].xx) lt--; 
        lt++; L[lt]=i;
    }
    else
    {
        while(rt&&a[R[rt]].x>a[i].x) rt--;
        rt++; R[rt]=i;
    }
}
int Find(int Y)
{
    if(!lt) return N+1;
    if(a[L[lt]].yreturn N+1;
    int lf=1; int rg=lt;
    while(lf+1int mid=(lf+rg)>>1;
        if(a[L[mid]].y>Y) rg=mid;
        else lf=mid;
    }
    if(a[L[lf]].y>Y) return lf;
    else return rg;
}
void solve(int lf,int rg)
{
    if(lf==rg) return;
    int mid=(lf+rg)>>1;
    solve(lf,mid); solve(mid+1,rg);
    lt=0; rt=0; int tmp=lf;
    for(int i=mid+1;i<=rg;i++)
    {
        while(tmp<=mid&&a[tmp].yy) {Add(tmp,0); tmp++;}
        Add(i,1);
        int pos; if(rt>1) pos=Find(a[R[rt-1]].y); else pos=Find(-1);
        ret+=1LL*max(0,lt-pos+1);
    }
    int p1=lf; int p2=mid+1; int cnt=0;
    while(p1<=mid&&p2<=rg)
    {
        if(a[p1].yy) {b[lf+cnt]=a[p1]; p1++; cnt++;}
        else{b[lf+cnt]=a[p2]; p2++; cnt++;}
    }
    while(p1<=mid) {b[lf+cnt]=a[p1]; p1++; cnt++;}
    while(p2<=rg) {b[lf+cnt]=a[p2]; p2++; cnt++;}
    for(int i=lf;i<=rg;i++) a[i]=b[i];
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d%d",&a[i].x,&a[i].y);
    sort(a+1,a+n+1,cmp);
    solve(1,n);
    printf("%lld\n",ret);

    return  0;
}

你可能感兴趣的:(&,图论,题解,bzoj,cdq分治&整体二分)