窗内的星星【线段树+扫描线+离散化+lazy标记】
POJ2482
、ACwing248
题目:
在一个天空中有很多星星(看作平面直角坐标系),已知每颗星星的坐标和亮度(都是整数)。
求用宽为 W、高为 H 的矩形窗口(W,H 为正整数)能圈住的星星的亮度总和最大是多少。(矩形边界上的星星不算)
输入格式:
输入包含多组测试用例。
每个用例的第一行包含 3 个整数:n,W,H,表示星星的数量,矩形窗口的宽和高。
然后是 n 行,每行有 33 个整数:x,y,c,表示每个星星的位置 (x,y) 和亮度。
没有两颗星星在同一点上。
输出格式:
每个测试用例输出一个亮度总和最大值。
每个结果占一行。
数据范围:
1≤n≤10000,
1≤W,H≤1000000,
0≤x,y<231
输入样例:
3 5 4
1 2 3
2 3 2
6 3 1
3 5 4
1 2 3
2 3 2
5 3 1
输出样例:
5
6
代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e4+5;
int n,W,H,k;
//----------------------------------------
vector<int> v; //用于y轴离散化
int find_w(int x){
//找到离散化后代表x的数字
return lower_bound(v.begin(),v.end(),x) - v.begin();
}
//----------------------------------------
struct Edge{
int x,y1,y2,c; //分别表示星星的坐标和亮度
} e[N << 2]; //存储四元组
bool cmp(Edge a,Edge b){
if(a.x == b.x)
return a.c < 0;
return a.x < b.x;
} //按照x排序,方便从左到右扫描
//----------------------------------------
struct T{
int l,r; //区间
int ans,lazy; //答案、lazy标记
} tree[N << 3]; //存储线段树
void build(int p,int l,int r){
//建立线段树
tree[p] = {
l,r,0,0};
if(l == r)
return;
int lc = p<<1,rc = p<<1|1,mid = l+r>>1;
build(lc,l,mid);
build(rc,mid + 1,r);
}
void push_down(int p){
//消除lazy标记
int lc = p<<1,rc = p<<1|1,lazy = tree[p].lazy;
tree[lc].ans += lazy;
tree[lc].lazy += lazy;
tree[rc].ans += lazy;
tree[rc].lazy += lazy;
tree[p].lazy = 0;
}
void update(int p,int l,int r,int c){
//如果全覆盖的话,可以不着急更新子结点,使用lazy标记做记录
if(tree[p].l == l && tree[p].r == r){
tree[p].lazy += c;
tree[p].ans += c;
return;
}
push_down(p); //如果没有全覆盖,则应该线更新子节点后再访问子节点
int lc = p<<1,rc = p<<1|1,mid = tree[p].l+tree[p].r>>1;
if(r <= mid)
update(lc,l,r,c);
else if(l > mid)
update(rc,l,r,c);
else
update(lc,l,mid,c),update(rc,mid + 1,r,c);
tree[p].ans = max(tree[lc].ans,tree[rc].ans); //更新该结点
}
signed main(){
int x,y,c;
while(cin>>n>>W>>H){
k = 0;
v.clear();
for(int i = 0;i < n;++i){
cin>>x>>y>>c;
e[++k] = {
x,y,y + H - 1,c};
e[++k] = {
x + W,y,y + H - 1,-c};
v.push_back(y),v.push_back(y + H - 1);
}
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end()); //去重离散化
sort(e + 1,e + k + 1,cmp); //排序,以便后续扫描
build(1,0,v.size() - 1); //建立线段树
int ans = 0;
for(int i = 1;i <= k;++i){
//扫描到这条边的时候根据c的值考虑是要加入这个星星的亮度还是减去这个星星的亮度
update(1,find_w(e[i].y1),find_w(e[i].y2),e[i].c);
//更新ans
ans =max(ans,tree[1].ans);
}
cout<<ans<<"\n";
}
return 0;
}