窗内的星星【线段树+扫描线+离散化+lazy标记】

窗内的星星【线段树+扫描线+离散化+lazy标记】

POJ2482ACwing248

题目:

在一个天空中有很多星星(看作平面直角坐标系),已知每颗星星的坐标和亮度(都是整数)。

求用宽为 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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Alan_Lowe

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值