bzoj4237: 稻草人 cdq分治 单调栈

本文介绍了一种解决稻草人问题的有效算法,通过CDQ分治策略,将时间复杂度从n^2优化至更高效的状态。文章详细解释了如何通过分治思想,结合单调栈和上凸壳技巧,对区间贡献进行统计,从而快速求解问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >



题目链接

bzoj4237: 稻草人

题解

暴力统计是n^2的
考虑统计一段区间对另一端的贡献
对于y值cdq分治,降调一维
对于当前两个分治区间统计上面那部分对下面那部分的贡献
对当前两区间x排序后,对上部分维护单增单调栈,得到距离当前点最近的比她低的点p
对于下面的区间维护一个上凸壳 ,直接在凸壳上二分p统计答案

代码

#include<set> 
#include<cstdio> 
#include<cstring> 
#include<algorithm> 
#define gc getchar()
#define pc putchar 
#define LL long long 
inline int read() { 
    int x = 0,f = 1; 
    char c = gc; 
    while(c < '0' || c > '9') c = gc; 
    while(c <= '9' && c >= '0') x = x * 10 + c - '0',c = gc; 
    return x * f;  
} 
void print(LL x) { 
    if(x < 0) { 
        pc('-'); 
        x = -x; 
    } 
    if(x >= 10) print(x / 10); 
    pc(x % 10 + '0'); 
} 
const int maxn = 200007; 
struct Point {
    int x,y; 
} po[maxn]; 
int n; 
bool cmpx(Point a,Point b) { return a.x < b.x;  } 
bool cmpy(Point a,Point b) { return a.y < b.y;  } 
int tp,tl; 
int sk[maxn],sk2[maxn]; 
LL  ans = 0; 
void solve(int l = 1,int r = n) {
    if(l == r) return ; 
    int mid = l + r >> 1; 
    std::sort(po + l,po + r + 1,cmpy); 
    std::sort(po + l,po + mid + 1,cmpx); //down
    std::sort(po + mid + 1,po + r + 1,cmpx); //up 
    tp = tl = 0; 
    int p = l,L,R,to,miid,lim;  
    for(int i = mid + 1;i <= r;++ i) { 
        while(tp && po[sk[tp]].y >= po[i].y) tp --; 
        sk[++ tp] = i; 
        
        for(;p <= mid && po[p].x < po[i].x;++ p) { 
            while(tl && po[sk2[tl]].y <= po[p].y) tl --; 
            sk2[++ tl] = p;  
        } 
        
        L = 1,R = tl;to = - 1;lim = po[sk[tp - 1]].x; 
        while(L <= R) { 
            miid = L + R >> 1; 
            if(po[sk2[miid]].x > lim) to = miid,R = miid - 1; 
            else L = miid + 1; 
        }  
        if(to != -1) ans += 1ll * tl - 1ll * to + 1; 
    } 
    solve(l,mid); solve(mid + 1,r); 
} 
int main() { 
    n = read(); 
    for(int i = 1;i <= n;++ i) { 
        po[i].x = read(),po[i].y = read(); 
    } 
    po[0].x = po[0].y = -1; 
    solve(); 
    print(ans); 
    pc('\n'); 
} 

转载于:https://www.cnblogs.com/sssy/p/9715479.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值