POJ 2482 Stars in Your Window (扫描线+线段树+离散化)

题目链接:http://poj.org/problem?id=2482

题目大意:有n颗星星,每颗星星都有一个权值。另有一个w*h的矩形,求矩形能围住的星星的权值和的最大值(在矩形边界上的星星不算)。

思路:题目描述这么感人,然而题目却那么无情。。想了两天也没思路,最后看了题解才懵懂。

           假设没有y坐标这一维,所有星星都在数轴上,而矩形也就变成了一条线段。那么易知,如果两个点相距小于w(原矩形的宽),那么这两个点可以被线段同时包围。一维的情况则可以有如下解法:应用线段树,对于每个点(x, 0),在线段树中将区间[x, x+w-1]中的每个点都加上该点的权值(对应于线段树的成段更新),那么最终的答案就是整个区间中权值最大的点,所以线段树要维护区间最大值。

          对应二维的情况,我们仍然可以遵循上面的思路。对应每一个高度为y,权值为v的点,我们在高度y+h出,放上一个权值为-w的点。然后将所有点按照y坐标排序,用平行于x轴的扫描线扫描,对于每一个点(x, y),更新区间[x, x+w-1]。要注意的是,若扫描线上既有权值正负的点都有,应先更新权值为负的,因为这个点已经失效了。

         PS:本题坐标范围太大,应对x坐标进行离散化处理。

#include<cstdio>
#include<cstring>
#include<string>
#include<cctype>
#include<iostream>
#include<set>
#include<map>
#include<vector>
#include<stack>
#include<queue>
#include<algorithm>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
typedef long long ll;
using namespace std;
const int maxn = 20000 + 5;
ll X[maxn];
int Max[maxn<<2], add[maxn<<2];

struct Line {
  ll x1, x2, y;
  int bri;
  Line() {}
  Line(ll y, ll x1, ll x2, int bri) : y(y), x1(x1), x2(x2), bri(bri) {}
  bool operator < (const Line& rhs) const {
    if(y != rhs.y) return y < rhs.y;
    return bri < rhs.bri;
  }
}line[maxn];

void PushDown(int rt) {
  if(add[rt]) {
    add[rt<<1] += add[rt];
    add[rt<<1|1] += add[rt];
    Max[rt<<1] += add[rt];
    Max[rt<<1|1] += add[rt];
    add[rt] = 0;
  }
}

void PushUp(int rt) {
  Max[rt] = max(Max[rt<<1], Max[rt<<1|1]);
}

void build(int l, int r, int rt) {
  if(l == r) {
    Max[rt] = 0;
    add[rt] = 0;
    return;
  }
  int m = (l + r) >> 1;
  build(lson);
  build(rson);
  Max[rt] = add[rt] = 0;
}

void update(int ql, int qr, int x, int l, int r, int rt) {
  if(ql <= l && r <= qr) {
    add[rt] += x;
    Max[rt] += x;
    return;
  }
  PushDown(rt);
  int m = (l + r) >> 1;
  if(ql <= m) update(ql, qr, x, lson);
  if(m < qr) update(ql, qr, x, rson);
  PushUp(rt);
}

int main() {
  int n, W, H;
  while(scanf("%d%d%d", &n, &W, &H) == 3) {
    int cnt1 = 0, cnt2 = 0;
    for(int i = 0; i < n; i++) {
      ll x, y;
      int bri;
      scanf("%lld%lld%d", &x, &y, &bri);
      X[cnt1++] = x;
      X[cnt1++] = x+W;
      line[cnt2++] = Line(y, x, x+W, bri);
      line[cnt2++] = Line(y+H, x, x+W, -bri);
    }
    sort(X, X+cnt1); int len = unique(X, X+cnt1) - X;
    sort(line, line+cnt2);
    
    int N = cnt1;
    build(1, N, 1);
    
    int mx = 0;
    for(int i = 0; i < cnt2; i++) {
      int l = upper_bound(X, X+cnt1, line[i].x1) - X; //X坐标从0开始,而我习惯的线段树坐标从1开始,所以用了upper_bound
      int r = upper_bound(X, X+cnt1, line[i].x2) - X;
      update(l, r-1, line[i].bri, 1, N, 1); //这里应为r-1
      mx = max(mx, Max[1]);
    }
    printf("%d\n", mx);
  }
  return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值