【JZOJ4925】【GDOI2017模拟12.18】稻草人

题目描述

YLOI村有一片荒地,上面竖着N个稻草人,村民们每年多次在稻草人们的周围举行祭典。
有一次,YLOI村的村长听到了稻草人们的启示,计划在荒地中开垦一片田地。和启示中的一样,田地需要满足以下条件:
1、田地的形状是边平行于坐标轴的长方形;
2、左下角和右上角各有一个稻草人;
3、田地的内部(不包括边界)没有稻草人。
给出每个稻草人的坐标,请你求出有多少遵从启示的田地的个数

数据范围

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)互不相同。

=w=

给所有稻草人,按横坐标从小到大排序。
然后对横坐标进行分治,ans(l,r)=ans(l,mid)+ans(mid+1,r)+Ans
其中Ans表示,跨越x[mid](也就是中线)的答案数。


维护两个稻草人单调栈:
第一个维护的稻草人,横坐标递减。
第二个维护的稻草人,横坐标递增。
把区间内的稻草人,按纵坐标从大到小加入。
分情况讨论:
1.如果加入的是在中线之右的,那么把他加入第一个单调栈中。
2.如果加入的是在中线之左或之上的,那么把他加入第二个单调栈中;
紧接着,加入的这个稻草人,得出它在第二个单调栈中的上一个稻草人的纵坐标Y,那么在第一个单调栈中所有纵坐标在Y之下的,都可以与这个稻草人贡献一个答案。


提示:分治中可以套归并排序。
总的时间复杂度为O(nlog2n2)O(nlog2n)分治,O(log)归并排序。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define ll long long
#define sqr(x) ((x)*(x))
#define ln(x,y) ll(log(x)/log(y))
using namespace std;
const char* fin="scarecrows.in";
const char* fout="scarecrows.out";
const ll inf=0x7fffffff;
const ll maxn=200007;
ll n,i,j,k,ans;
struct node{
    ll x,y;
    void operator =(const node &b){
        x=b.x;
        y=b.y;
    }
}a[maxn],b[maxn];
bool cmp(node a,node b){
    return a.x<b.x;
}
ll x[maxn];
ll st1[maxn],st2[maxn],num1,num2;
void push1(ll v){
    while (num1 && a[v].x>a[st1[num1]].x) num1--;
    st1[++num1]=v;
}
void push2(ll v){
    while (num2 && a[v].x<a[st2[num2]].x) num2--;
    st2[++num2]=v;
}
ll mergesort(ll l,ll r){
    ll i,j,k,mid=(l+r)/2,Mid=x[mid];
    if (l==r) return 0;
    ll tmp=mergesort(l,mid)+mergesort(mid+1,r);
    i=l;
    j=mid+1;
    k=l;
    while (i<=mid && j<=r)
        if (a[i].y>a[j].y) b[k++]=a[j++];
        else b[k++]=a[i++];
    while (i<=mid) b[k++]=a[i++];
    while (j<=r) b[k++]=a[j++];
    num1=num2=0;
    for (i=r;i>=l;i--){
        a[i]=b[i];
        if (a[i].x<=Mid){
            push1(i);
            if (num1>1){
                j=a[st1[num1-1]].y;
                if (num2){
                    ll lef=1,rig=num2,mm;
                    while (lef<rig){
                        mm=(lef+rig)/2;
                        if (a[st2[mm]].y<=j) rig=mm;
                        else lef=mm+1;
                    }
                    if (a[st2[lef]].y<=j) tmp+=num2-lef+1;
                }
            }else tmp+=num2;
        }else push2(i);
    }
    return tmp;
}
int main(){
    freopen(fin,"r",stdin);
    freopen(fout,"w",stdout);
    scanf("%lld",&n);
    for (i=1;i<=n;i++) scanf("%lld%lld",&a[i].x,&a[i].y),x[i]=a[i].x;
    sort(x+1,x+n+1);
    sort(a+1,a+n+1,cmp);
    ans=mergesort(1,n);
    printf("%lld",ans);
    return 0;
}

=o=

一开始想的时候,其实也想到了:
把纵坐标从大到小加入;

计算所有稻草人作为左下角的贡献:
先想想对于一个稻草人a,如果要与另一个稻草人b贡献答案,存在什么样的条件:
1.b要比a更高,否则不满足a作为左下角的条件;
2.a,b构成的矩阵中不能有稻草人
在这里,先考虑满足其中某个条件,在对另一个条件进行讨论。
而“把纵坐标从大到小加入”正好达到了这个目的。

现在,只需要满足第二个条件就可以了。

第二个条件容易转化成“不存在一个稻草人在a的右上方,并且位于b的左下方”。
事实上,这个也是要满足多个条件。
1.a的右边
2.a的上边
3.b的下边
4.b的左边
所以我们考虑先满足其中的一个或几个。
我们考虑到可以使用分治,再维护两个单调栈来达成目的。

转载于:https://www.cnblogs.com/hiweibolu/p/6714810.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值