BZOJ4237: 稻草人

9 篇文章 0 订阅

CDQ分治,按照横坐标排序,二分高度,按mid将当前处理的点分成两部分,计算高度>mid作为右上角的对高度≤mid的作为左下角的贡献
横坐标从右往左扫,因为田地内部不能有稻草人,所以对于右上角要求从右往左高度递增,对于左下角要求从右往左高度递增,所以维护两个单调队列
这里要注意几个问题,
对于一个上方的点,如果他比右边的同类点矮,那么要找到他右边第一个比他矮的同类点重新统计对当前左下角的点的贡献
对于一个作为左下角的点,因为他的右边比他高的同类点会挡住一些上方的点,所以计算对他的贡献时,二分找到他右边第一个比他高的同类点,从这个点的左边的第一个上方点开始计算对当前左下角点的贡献

题解后来写的,代码和题解的做法不一样,复杂些,效率比较低下,不推荐参考…
code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

const int maxn = 210000;
struct node
{
    int x,y;
    node(){}
    node(int _x,int _y){x=_x;y=_y;}
}a[maxn];
int n;
ll re;
bool cmp(node x,node y){return x.x<y.x;}
bool cmp2(node x,node y){return x.y<y.y;}

int q[maxn],q2[maxn];
int tc(int x,int l,int r)
{
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(a[q[mid]].x>x) l=mid+1;
        else r=mid-1;
    }
    return l-1;
}
int tc2(int x,int l,int r)
{
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(a[q2[mid]].y>x) l=mid+1;
        else r=mid-1;
    }
    return l-1;
}
void CDQ(int l,int r)
{
    if(l==r) return ;
    int mid=(l+r)>>1;
    CDQ(l,mid);
    CDQ(mid+1,r);

    int d=mid,j=r,la=0,ed=n;
    int tail=0,tail2=0;
    node *x=&a[d];
    for(int i=d;i>=l;i--,x--)
    {
        while(tail2&&(*x).y>a[q2[tail2]].y) tail2--;
        while(j>d&&a[j].x>(*x).x)
        {
            while(tail&&a[j].y<a[q[tail]].y) tail--;
            q[++tail]=j;
            j--;
        }
        int t2=tc2((*x).y,1,tail2);
        if(t2)
        {
            t2=tc(a[q2[t2]].x,1,tail);
        }
        re+=(ll)(tail-t2);
        q2[++tail2]=i;
        la=(*x).y;
    }

    sort(a+l,a+r+1,cmp);
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        int x,y; scanf("%d%d",&x,&y);
        a[i]=node(x,y);
    }
    sort(a+1,a+n+1,cmp);
    for(int i=1;i<=n;i++) a[i].x=i;
    sort(a+1,a+n+1,cmp2);
    for(int i=1;i<=n;i++) a[i].y=i;

    re=0ll;
    CDQ(1,n);
    printf("%lld\n",re);

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值