题目链接: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;
}