窗内的星星【线段树+扫描线+离散化+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;
}

猜你喜欢

转载自blog.csdn.net/qq_45985728/article/details/123365903