HDU 5091 Beam Cannon(线段树)

题目:Beam Cannon

题意:给定一个二维平面上的N个点的坐标,要用一个w*h的矩形,问这个矩形最多能罩住多少个点,在边界上也算。

用线段树来做。

对于一个点(x,y),一个矩形能够罩住它的话,首先矩形的右边界必须在x和x+w之间。反过来,这个点能够影响到的范围也就是[x, x+w]。我们可以在添加这个点的时候将[x, x+w]这个区间全部加上1。那么这样最多能覆盖多少个点就变成线段树求最大值的问题了。

这里为了方便计算,先把点全部向右平移20001,x坐标的范围就从[-20000,20000]变成[1, 40001]

再来考虑高度的问题,矩形内的点的高度差不能超过h。

我们可以将点按照y的从小到大排序。然后遍历点,对于当前遍历到的点,我们需要将跟它高度差超过h的删除。我们可以用一个队列来处理,每次把点的信息添加到线段树中时,就顺便把点添加到队列中。每次把队列里面跟当前点高度差大于h的点取出,然后从线段树中把它删除。

每次添加一个点就维护一次答案。当所有点添加过之后,最优答案自然也就出来了。

具体细节看代码吧。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int base = 20001;
const int N = 10000;
const int M = 40001;
#define lson o<<1
#define rson (o<<1)|1
int n, w, h;
struct Point{
    int x, y;
    bool operator < (const Point &A)const{
        return y<A.y || (y==A.y&&x<A.x);
    }
}p[N];
int s[M<<2], l[M<<2], r[M<<2], f[M<<2];
void build(int o, int ll, int rr){
    s[o]=f[o]=0;
    l[o]=ll; r[o]=rr;
    if(ll<rr){
        int m = (ll+rr)>>1;
        build(lson, ll, m);
        build(rson, m+1, rr);
    }
}
void maintain(int o){
    s[o] = max(s[lson], s[rson]);
}
void update(int o, int ll, int rr, int v);
void pushdown(int o){
    if(f[o]){
        int m = (l[o]+r[o])>>1;
        update(lson, l[o], m, f[o]);
        update(rson, m+1, r[o], f[o]);
        f[o] = 0;
    }
}
void update(int o, int ll, int rr, int v){
    if(l[o]==ll && r[o]==rr){
        s[o] += v;
        f[o] += v;
        return;
    }
    pushdown(o);
    int m = (l[o]+r[o])>>1;
    if(rr<=m)   update(lson, ll, rr, v);
    else if(ll>m)   update(rson, ll, rr, v);
    else{
        update(lson, ll, m, v);
        update(rson, m+1, rr, v);
    }
    maintain(o);
}
int main(){
    while(~scanf("%d", &n) && n>=0){
        scanf("%d %d", &w, &h);
        for(int i=0; i<n; i++){
            scanf("%d %d", &p[i].x, &p[i].y);
            p[i].x += base;
        }
        sort(p, p+n);
        int ans = 0;
        build(1, 1, M);
        queue<Point> Q;
        for(int i=0; i<n; i++){
            while(!Q.empty() && p[i].y-Q.front().y > h){
                int l = Q.front().x;
                int r = min(l+w, M-1);
                update(1, l, r, -1);
                Q.pop();
            }
            int l = p[i].x;
            int r = min(l+w, M-1);
            update(1, l, r, 1);
            ans = max(ans, s[1]);
            Q.push(p[i]);
        }
        printf("%d\n", ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值