P1502 窗口的星星 离散化+扫描线

题意:
一个二维平面上有些点有权值。
问给你一个H*W的窗口,问窗口星星亮度总和的最大值。
H ∗ W 范 围 : 1 e 6 H*W 范围:1e6 HW1e6
坐 标 范 围 : 1 e 9 坐标范围:1e9 1e9
星 星 数 目 : 1 e 4 星星数目:1e4 1e4

题解:
我们考虑一维的情况。
假设一个序列 a a a,给你你最多可以选择连续长度为 l e n len len序列段,问可以选择出最大的序列和为多少。
我们可以将这个(单点修改,区间查询)的问题给转换成,(区间修改,单点查询)问题。
初始化一个新数组 b b b为0
对于第 i i i个值来说,我们把 b b b的第 i i i到第 i + l e n i+len i+len的值加上 a [ i ] a[i] a[i]
然后查询b序列的最大值即为答案

同理我们可以得出在二维平面上的结论。
那么我们把该点 ( i , j ) (i,j) (i,j)权值,在 [ ( i , i + W ) , ( j , j + H ) ] [(i,i+W),(j,j+H)] [(i,i+W),(j,j+H)]的范围内每个点都加上。然后就是查询这个新生成矩阵的最大值的点是哪个即可。

如何查询?
扫描线。
说是扫描线,不过这个扫描线是统计最大值点的,并不是统计面积的。
所以原来扫描线的模板不可以照搬。
也可以理解为线段树+离散化。

还有需要灵活处理的一点就是边框上的点是算不进去的,那你把窗口的长宽各减去1即可。

比较坑的一点:对 x 坐标进行升序排序时,将 val 值按降序排序,这样才能处理两个矩形贴合的情况。

代码:

#include<bits/stdc++.h>
#define endl '\n'
//#define int long long
using namespace std;

const int maxn=4e5;

struct L{
    int x;
    int y1,y2;
    int flag;
    bool operator <(const L & a)const{
        if(x!=a.x) return x<a.x;
        else return flag>a.flag;
    }
}line[maxn];

int v[maxn];
int tree[maxn],lazy[maxn];
void push_down(int node){
    if(lazy[node]){
        tree[node<<1]+=lazy[node];
        tree[node<<1|1]+=lazy[node];
        lazy[node<<1]+=lazy[node];
        lazy[node<<1|1]+=lazy[node];
        lazy[node]=0;
    }
}

void update(int node,int start,int ends,int l,int r,int val){
    if(l<=start&&ends<=r){
        tree[node]+=val;
        lazy[node]+=val;
        return;
    }
    push_down(node);
    int mid=(start+ends)>>1;
    if(l<=mid) update(node<<1,start,mid,l,r,val);
    if(mid<r) update(node<<1|1,mid+1,ends,l,r,val);
    tree[node]=max(tree[node<<1],tree[node<<1|1]);
}


signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    int t;
    cin>>t;
    while(t--){
        int n,h,w;
        cin>>n>>w>>h;
        for(int i=1;i<=n;i++){
            int x,y,z;
            cin>>x>>y>>z;
            int x1=x+w-1;
            int y1=y+h-1;
            v[i]=y,v[i+n]=y1;
            line[i]={x,y,y1,z};
            line[i+n]={x1,y,y1,-z};
        }
        sort(v+1,v+1+2*n);
        sort(line+1,line+1+2*n);
        int tot=unique(v+1,v+1+2*n)-v-1;
        for(int i=1;i<=2*n;i++){
            int x=lower_bound(v+1,v+tot+1,line[i].y1)-v;
            int y=lower_bound(v+1,v+tot+1,line[i].y2)-v;
            line[i].y1=x;
            line[i].y2=y;
        }
        int ans=0;
        for(int i=1;i<=2*n;i++){
            update(1,1,tot,line[i].y1,line[i].y2,line[i].flag);
            ans=max(ans,tree[1]);
        }
        memset(tree,0,sizeof tree);
        memset(lazy,0,sizeof lazy);
        cout<<ans<<endl;
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值