bzoj 4237: 稻草人 树状数组+分治

分治的时候我们只需要右上点在右半个,左下点在左半个即可,然后左边每个点可以用set求出它所能贡献的y的区间,对于右半边可以求出他能取到的区间,然后排个序用two points+树状数组维护一下即可。

#include <cstdio>
#include <cstring>
#include <vector>
#include <iostream>
#include <algorithm>
#include<set>
#include<queue>
using namespace std;
typedef pair<int,int>p1;
const int maxn=200005;
struct pi{
    int x,y;
}pp[maxn],pp1[maxn],pp2[maxn];
typedef long long LL;
LL ans;
int y[maxn];
int bit[maxn];
int low(int p){
    return p&(-p);
}
void merg(int p,int n,int k){
    while (p<=n) {
        bit[p]+=k;
        p=p+low(p);
    }
}
int sum(int p){
    int s=0;
    while(p){
        s+=bit[p];
        p=p-low(p);
    }
    return s;
}
set<int>ss;
int cmp(pi a,pi b){
    return a.x<b.x;
}
int cmp1(pi a,pi b){
    return a.y<b.y;
}
int all[maxn];
int n;
void divide(int l,int r){
    if(l==r) return;
    int mid=(l+r)/2;
    divide(l,mid);
    divide(mid+1,r);
    ss.clear();
    ss.insert(n+1);
    int tot=0;
    for(int i=mid;i>=l;i--){
        set<int>::iterator it;
        it=ss.lower_bound(pp[i].y);
        pp1[tot].x=pp[i].y;
        pp1[tot].y=*it;
        tot++;
        ss.insert(pp[i].y);
    }
    ss.clear();
    ss.insert(0);
    int cnt=0;
    for(int i=mid+1;i<=r;i++){
        set<int>::iterator it;
        it=ss.lower_bound(pp[i].y);
        it--;
        pp2[cnt].x=*it;
        pp2[cnt].y=pp[i].y;
        cnt++;
        ss.insert(pp[i].y);
    }
    sort(pp1,pp1+tot,cmp1);
    sort(pp2,pp2+cnt,cmp1);
    for(int i=0;i<tot;i++) merg(pp1[i].x,n,1);
    int p=0;
    for(int i=0;i<cnt;i++){
        while(p<tot&&pp1[p].y<=pp2[i].y){
            merg(pp1[p].x,n,-1);
            p++;
        }
        ans+=sum(pp2[i].y)-sum(pp2[i].x);
    }
    while (p<tot) {
        merg(pp1[p].x,n,-1);
        p++;
    }
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++){
        scanf("%d%d",&pp[i].x,&pp[i].y);
        y[i]=pp[i].y;
    }
    sort(y+1,y+1+n);
    for(int i=1;i<=n;i++){
        pp[i].y=lower_bound(y+1,y+1+n,pp[i].y)-y;
    }
    sort(pp+1,pp+1+n,cmp);
    ans=0;
    divide(1,n);
    cout<<ans<<endl;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值