P1502 窗口的星星(扫描线入门第一题)
Posted caijiaming
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P1502 窗口的星星(扫描线入门第一题)相关的知识,希望对你有一定的参考价值。
题目链接:https://www.luogu.org/problem/P1502
P1502 窗口的星星
标签
相关讨论
进入讨论版推荐题目
题目背景
小卡买到了一套新房子,他十分的高兴,在房间里转来转去。
题目描述
晚上,小卡从阳台望出去,“哇~~~~好多星星啊”,但他还没给其他房间设一个窗户,天真的小卡总是希望能够在晚上能看到最多最亮的星星,但是窗子的大小是固定的,边也必须和地面平行。这时小卡使用了超能力(透视术)知道了墙后面每个星星的位置和亮度,但是小卡发动超能力后就很疲劳,只好拜托你告诉他最多能够有总和多亮的星星能出现在窗口上。
输入格式
本题有多组数据,第一行为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个整数,表示每组数据中窗口星星亮度总和的最大值。
输入输出样例
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
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 窗口的星星(扫描线入门第一题)的主要内容,如果未能解决你的问题,请参考以下文章