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

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

P1502 窗口的星星

提交2.78k

通过682

时间限制1.00s

内存限制125.00MB

提交代码加入收藏

题目提供者cyrcyr

难度省选/NOI-

历史分数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\n",ans);
    }
    return 0;
}
/**
2
3 5 4
1 2 3
2 3 2
6 3 1
*/

原文地址:https://www.cnblogs.com/caijiaming/p/11504308.html

时间: 2024-10-08 03:43:02

P1502 窗口的星星(扫描线入门第一题)的相关文章

luogu P1502 窗口的星星

题目链接 P1502 窗口的星星 题解 扫描线+线段树 线段树的每一个节点处理的是左边框放在当前x-1位置时的框内星星的亮度大小 按照x坐标进行离散化,得到离散化后每一个坐标x的可影响的范围 维护扫描线,扫到可以加某颗星星就把星星加进去,扫到该出来的时候就把星星搞出来,线段树维护一下 代码 #include<cstdio> #include<cstring> #include<algorithm> const int maxn = 100007; int n,w,h;

CTF---编程入门第一题 循环

循环分值:10 来源: 北邮天枢战队 难度:易 参与人数:1478人 Get Flag:467人 答题人数:523人 解题通过率:89% 给出一个循环公式,对于一个整数n,当n为奇数时,n=3n+1,当n为偶数时,n=n/2,如此循环下去直到n=1时停止. 现要求对两个整数i = 900.j = 1000,输出i.j之间(包括i.j)的所有数进行上述循环时的最大循环次数(包括n和1). 格式:CTF{xxx} 解题链接: 原题链接:http://www.shiyanbar.com/ctf/192

状压dp入门第一题 poj3254

题目链接 http://poj.org/problem?id=3254 转自http://blog.csdn.net/harrypoirot/article/details/23163485 1 #include <stdio.h> 2 #include <math.h> 3 #include <string.h> 4 #include <stdlib.h> 5 #include <iostream> 6 #include <sstream

状态压缩dp入门 第一题 POJ 3254 Corn Fields

Corn Fields Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 6460   Accepted: 3436 Description Farmer John has purchased a lush new rectangular pasture composed of M by N (1 ≤ M ≤ 12; 1 ≤ N ≤ 12) square parcels. He wants to grow some yumm

POJ-1273-Drainage Ditches:网络流入门第一题

#include<iostream> #include<cstring> #include<cstdio> #include<queue> using namespace std; #define Size 200 int Gra[Size+1][Size+1];// 由于是单向边 所以一个矩阵可以表示一个带有反向边的残余网络 bool Visited[Size+1]; int Pre[Size+1]; // 前驱节点 用来形成一条从源点到汇点的路径 int

java入门第一步之完成jdk的安装(window)【转】

为了面向更多的人类,我决定重温我的java起步: 要进行java开发第一步就是进行java环境的安装,也就是jdk的按装: 1.由于java被oracle收购了,我们下载jdk也就去oracle的官网进行下载http://www.oracle.com/technetwork/java/archive-139210.html 下载jdk的jdk应该是一个java文件夹,里面有jdk和jre两个文件夹,正确下载后我们一般是将该j整个文件放到C:\Program Files\目录下: 2.接下来我们开

HDU2063(二分匹配入门模板题)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2063 过山车 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 9322    Accepted Submission(s): 4108 Problem Description RPG girls今天和大家一起去游乐场玩,终于可以坐上梦寐以求

“金山杯2007逆向分析挑战赛”第一阶段第一题分析

题目来自于如下网址: http://www.pediy.com/kssd/ 第13篇 论坛活动 \ 金山杯2007逆向分析挑战赛 \ 第一阶段 \ 第一题 \ 题目 \ [第一阶段 第一题]: 现将此题目概述粘贴如下: CrackMe.exe 是一个简单的注册程序,见附件,请写一个注册机: 要求: 1. 注册机是KeyGen,不是内存注册机或文件Patch 2. 注册机可以使用ASM,VC,BC,VB,Delphi等语言书写,其他谢绝使用. 3. 注册机必须可以运行在Windows系统上. ..

迷宫问题 BFS入门水题

1102:迷宫问题 时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:84 解决: 41 题目描述 小明置身于一个迷宫,请你帮小明找出从起点到终点的最短路程. 小明只能向上下左右四个方向移动. 输入格式 输入包含多组测试数据.输入的第一行是一个整数T,表示有T组测试数据. 每组输入的第一行是两个整数N和M(1<=N,M<=100). 接下来N行,每行输入M个字符,每个字符表示迷宫中的一个小方格. 字符的含义如下: 'S':起点 'E':终点 '-':空地,可以通过 '#':障碍,无法通