P1502 窗口的星星(扫描线入门第一题)

Posted caijiaming

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P1502 窗口的星星(扫描线入门第一题)相关的知识,希望对你有一定的参考价值。

题目链接:https://www.luogu.org/problem/P1502

P1502 窗口的星星

提交2.78k
通过682
时间限制1.00s
内存限制125.00MB
题目提供者cyrcyr
历史分数100
 提交记录  查看题解

标签

 查看算法标签

相关讨论

进入讨论版
 查看讨论

推荐题目

 查看推荐
 展开

题目背景

小卡买到了一套新房子,他十分的高兴,在房间里转来转去。

题目描述

晚上,小卡从阳台望出去,“哇~~~~好多星星啊”,但他还没给其他房间设一个窗户,天真的小卡总是希望能够在晚上能看到最多最亮的星星,但是窗子的大小是固定的,边也必须和地面平行。这时小卡使用了超能力(透视术)知道了墙后面每个星星的位置和亮度,但是小卡发动超能力后就很疲劳,只好拜托你告诉他最多能够有总和多亮的星星能出现在窗口上。

输入格式

本题有多组数据,第一行为T 表示有T组数据T<=10

对于每组数据

第一行3个整数n,W,H,(n<=10000,1<=W,H<=1000000)表示有n颗星星,窗口宽为W,高为H。

接下来n行,每行三个整数xi,yi,li 表示星星的坐标在(xi,yi),亮度为li。(0<=xi,yi<2^31)

输出格式

T个整数,表示每组数据中窗口星星亮度总和的最大值。

输入输出样例

输入 #1
2
3 5 4
1 2 3
2 3 2
6 3 1
3 5 4
1 2 3
2 3 2
5 3 1
输出 #1
5
6

说明/提示

小卡买的窗户框是金属做的,所以在边框上的不算在内。

思路:就是扫描线的板子题,在这里记录一下自己理解的扫描线:

线段树维护y轴区间,x轴暴力,这是扫描线的精髓。 相当于一条条线沿着x轴扫过去,看一下为什么这样能解决问题:

把每一个星星作为左下角,在它的右上方划出一片窗口大小的区域,表示只要窗口的右上角落在这一片区域里就一定能覆盖到这颗星星。

那么不同星星的重叠部分就代表能同时覆盖这几颗星星了。

(下图中(这里是以星星为左下角,两种方式都行),只要窗口落在阴影部分,就能同时覆盖到三颗星星) 

 

 

 

技术图片

看代码:

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn=4e4+5;
typedef long long LL;
LL ly[maxn<<2];//存所有的y 离散化
LL lazy[maxn<<2],ma[maxn<<2];//线段树延迟标记  存最大值
int len;//
struct Node
{
    LL x,y,v,f,l,r;//x y v为坐标 f标记是起始边还是结束边  l r代表y轴区域
}node[maxn<<2];//存下所有的边
bool cmp(const Node a,const Node b)
{
    if(a.x==b.x) return a.f<b.f;//相等的话 先结束 再开始
    return a.x<b.x;
}
void Push_down(LL rt)
{
    ma[rt<<1]+=lazy[rt];
    ma[rt<<1|1]+=lazy[rt];
    lazy[rt<<1]+=lazy[rt];
    lazy[rt<<1|1]+=lazy[rt];
    lazy[rt]=0;
}
void Update(LL l,LL r,LL rt,LL L,LL R,LL v)
{
//    cout<<"l:"<<l<<" r:"<<r<<" L:"<<L<<" R:"<<R<<" v:"<<v<<endl;
    if(L<=l&&r<=R)
    {
        ma[rt]+=v;lazy[rt]+=v;return ;
    }
    LL mid=(l+r)>>1;
    if(lazy[rt]) Push_down(rt);
    if(L<=mid) Update(l,mid,rt<<1,L,R,v);
    if(R>mid) Update(mid+1,r,rt<<1|1,L,R,v);
    ma[rt]=max(ma[rt<<1],ma[rt<<1|1]);
}
LL getid(LL x)
{
    return lower_bound(ly+1,ly+len+1,x)-(ly);
}
int main()
{
    int T;scanf("%d",&T);
    while(T--)
    {
        memset(lazy,0,sizeof(lazy));memset(ma,0,sizeof(ma));
        LL N,W,H;scanf("%lld%lld%lld",&N,&W,&H);
        int p=0,p1=0;
        for(int i=1;i<=N;i++)
        {
            LL x,y,v;scanf("%lld%lld%lld",&x,&y,&v);
            ly[++p1]=y;
            node[++p].x=x;node[p].y=y;node[p].v=v;node[p].f=1;//起始边
            node[p].l=y;node[p].r=y+H-1;//这里的边界一定要注意 很有可能会错
            x=x+W;y=y+H-1;
            ly[++p1]=y;
            node[++p].x=x;node[p].y=y;node[p].v=v;node[p].f=-1;//结束边
            node[p].l=node[p-1].l;node[p].r=node[p-1].r;
        }
        sort(ly+1,ly+p1+1);
        len=unique(ly+1,ly+p1+1)-(ly+1);
        sort(node+1,node+p+1,cmp);
//        for(int i=1;i<=p;i++) cout<<node[i].x<<" "<<node[i].y<<" "<<node[i].v<<endl;
        LL ans=0;
        for(int i=1;i<=p;i++)// 遍历所有的边
        {
            LL l=node[i].l;
            LL r=node[i].r;
            l=getid(l);r=getid(r);
//            cout<<"i="<<i<<" f="<<node[i].f<<" val="<<node[i].v<<endl;
            Update(1,len,1,l,r,node[i].f*node[i].v);
            ans=max(ans,ma[1]);
        }
        printf("%lld
",ans);
    }
    return 0;
}
/**
2
3 5 4
1 2 3
2 3 2
6 3 1
*/

 

以上是关于P1502 窗口的星星(扫描线入门第一题)的主要内容,如果未能解决你的问题,请参考以下文章

P1502 窗口的星星(扫描线)

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

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

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

Luogu P1502 窗口的星星

leetcode 入门第一题 4ms? 8ms? Two Sum