bzoj 4237 稻草人

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4237

CDQ。然而却不会做。

**分治的话,只要考虑两部分之间对答案的贡献就行了,不用考虑部分内的。

所以就只考虑两部分之间构成的矩形。

考虑枚举上面部分或下面部分的点。不难(?)发现要使用单调栈。因为另一部分的种种限制很符合单调栈的样子。

同时自己枚举的这部分也要弄单调栈。这样就能知道本部分中第一个限制自己的点的位置,进而把这个位置到自己之间的所有另一部分符合要求的点加到答案里。

枚举上面部分的点的话,本部分限制自己的点在自己左边;而枚举下面部分的点的话,本部分限制点在右边,就得从右往左推了,比较不顺。所以枚举上面部分的点吧。

然后另一部分的点只保留在栈里的,二分找到上面部分的限制点(栈里当前点的上一个点)到当前点之间的部分,计入答案。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=2e5+5,INF=1e9+5;
int n,stack[N],top;
ll ans;
struct Node{
    int x,y;
    bool operator<(const Node &k)const
        {return x<k.x;}
}a[N];
bool cmp(Node u,Node v){return u.y<v.y;}
void cdq(int l,int r)
{
    if(l==r)return;
    int mid=((l+r)>>1);
    cdq(l,mid);cdq(mid+1,r);
    sort(a+l,a+mid+1,cmp);sort(a+mid+1,a+r+1,cmp);
    int k,u=mid+1;
    for(int i=l;i<=mid;i++)
    {    //下面那行导致n^2 
        for(k=i+1;k<=mid&&a[k].x<a[i].x;k++);if(k>mid)k=0;    //故底下赋初值 
        int mn=INF;
        while(a[u].y<a[i].y&&u<r)u++;if(a[u].y<a[i].y)break;
        for(int j=u;j<=r;j++)
        {
            if(a[j].y>a[k].y)break;
            if(a[j].x<mn)ans++,mn=a[j].x;    //故底下赋初值 
        }
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d%d",&a[i].y,&a[i].x);//
    sort(a+1,a+n+1);
    a[0].y=INF;a[0].x=INF;cdq(1,n);
    printf("%lld",ans);
    return 0;
}
以为自己写了个nlogn的,其实是n^2logn
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=2e5+5,INF=1e9+5;
int n,stack1[N],top1,stack2[N],top2;
ll ans;
struct Node{
    int x,y;
    bool operator<(const Node &k)const
        {return x<k.x;}
}a[N];
bool cmp(Node u,Node v){return u.y<v.y;}
void cdq(int l,int r)
{
    if(l==r)return;
    int mid=((l+r)>>1);
    cdq(l,mid);cdq(mid+1,r);
    sort(a+l,a+mid+1,cmp);sort(a+mid+1,a+r+1,cmp);
    top1=top2=0;int j=l;
    for(int i=mid+1;i<=r;i++)
    {
        while(top1&&a[stack1[top1]].x>a[i].x)top1--;stack1[++top1]=i;
        for(;j<=mid&&a[j].y<a[i].y;j++)
            {while(top2&&a[stack2[top2]].x<a[j].x)top2--;stack2[++top2]=j;}
        int L=1,R=top2,as=top2+1;    //如果stack2里一个也没有<==>下面的都在当前点的右边 
        while(L<=R)
        {
            int md=((L+R)>>1);    //下面那个是a[stack2[md]]!!!不是a[md]!!! 
            if(a[stack2[md]].y>a[stack1[top1-1]].y)as=md,R=md-1;    //此处需要对a[0].y赋初值 
            else L=md+1;
        }
        ans+=top2-as+1;
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d%d",&a[i].y,&a[i].x);//
    sort(a+1,a+n+1);
    a[0].y=-1;cdq(1,n);    //坐标>=0 
    printf("%lld",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/Narh/p/9231394.html