【Luogu 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个整数,表示每组数据中窗口星星亮度总和的最大值。

输入输出样例

输入样例#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轴的线就可以把这颗星星的贡献减掉了。(用Windows XP 画的的图,有点丑)

还有,星星的坐标很大,记得离散化。

具体操作细节可以看代码(用了vector来维护坐标上加和减的星星)

(代码虽然很长,但结构还算清晰吧)

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<cstdlib>
  5 #include<vector>
  6 #include<algorithm>
  7
  8 #define For(i,a,b) for(int i=a;i<=b;++i)
  9 #define Pn putchar(‘\n‘)
 10 #define llg long long
 11
 12 using namespace std;
 13
 14 const int N=2e4+10;
 15
 16 struct LIS{
 17     int x,y,id;
 18 }Lis[N*2];
 19
 20 struct Star{
 21     int x1,x2,y1,y2;
 22     llg lgt;
 23     Star(){
 24         x1=0; x2=0; y1=0; y2=0;
 25         lgt=0;
 26     }
 27 }st[N];
 28
 29 vector<int>ads[N];
 30 vector<int>mns[N];
 31
 32 int tot=0,n,m,W,H,x,y;
 33 llg tag[N*4],mx[N*4],ans=0;
 34
 35 void read(int &v){       //读入优化,和输出优化
 36     v=0; bool fg=0;
 37     char c=getchar(); if(c==‘-‘)fg=1;
 38     while(c<‘0‘||c>‘9‘){c=getchar(); if(c==‘-‘)fg=1;}
 39     while(c>=‘0‘&&c<=‘9‘){v=v*10+c-‘0‘,c=getchar();if(c==‘-‘)fg=1;}
 40     if(fg)v=-v;
 41 }
 42 void read(llg &v){
 43     v=0; bool fg=0;
 44     char c=getchar(); if(c==‘-‘)fg=1;
 45     while(c<‘0‘||c>‘9‘){c=getchar(); if(c==‘-‘)fg=1;}
 46     while(c>=‘0‘&&c<=‘9‘){v=v*10+c-‘0‘,c=getchar();if(c==‘-‘)fg=1;}
 47     if(fg)v=-v;
 48 }
 49 void write(int x){
 50     if(x>9)write(x/10);
 51     int xx=x%10;
 52     putchar(xx+‘0‘);
 53 }
 54                         //排序
 55 bool cmpX(const LIS &a,const LIS &b){
 56     return a.x<b.x;
 57 }
 58 bool cmpY(const LIS &a,const LIS &b){
 59     return a.y<b.y;
 60 }
 61                       //线段树操作
 62 void pDown(int o){
 63     llg tg=tag[o];  tag[o]=0;
 64     int ls=o<<1,rs=o<<1|1;
 65     tag[ls]+=tg; tag[rs]+=tg;
 66     mx[ls]+=tg; mx[rs]+=tg;
 67 }
 68 void Ins(int o,int l,int r,int lx,int rx,llg dt){
 69     if(lx<=l&&rx>=r){
 70         mx[o]+=dt; tag[o]+=dt;
 71         return;
 72     }
 73     int m=(l+r)>>1;
 74     int ls=o<<1,rs=o<<1|1;
 75     if(tag[o])pDown(o);
 76     if(lx<=m)Ins(ls,l,m,lx,rx,dt);
 77     if(rx>m)Ins(rs,m+1,r,lx,rx,dt);
 78     mx[o]=max(mx[ls],mx[rs]);
 79 }
 80
 81 int main(){
 82     int T; read(T);
 83     while(T--){
 84         tot=0; ans=0;
 85         memset(tag,0,sizeof(tag));
 86         memset(mx,0,sizeof(mx));
 87
 88         read(n); read(W); read(H);
 89         For(i,1,n){                        //存下星星区域的右上角和左下角
 90             read(x); read(y); read(st[i].lgt);
 91             st[i].x1=st[i].x2=st[i].y1=st[i].y2=0;
 92             Lis[++tot].x=x;
 93             Lis[tot].y=y,Lis[tot].id=i;
 94
 95             Lis[++tot].x=x+W-1;
 96             Lis[tot].y=y-H+1,Lis[tot].id=i;
 97         }
 98         Lis[0].x=-2147483600;
 99         Lis[0].y=-2147483600;
100
101         sort(Lis+1,Lis+tot+1,cmpY);        //分别对X和Y离散化
102         int ty=0;
103         For(i,1,tot){
104             if(Lis[i].y!=Lis[i-1].y)ty++;
105             int ID=Lis[i].id;
106             if(!st[ID].y2){
107                 st[ID].y2=ty;
108             }else{
109                 st[ID].y1=ty;
110             }
111         }
112
113         sort(Lis+1,Lis+tot+1,cmpX);
114         int tx=0;
115         For(i,1,tot){
116             if(Lis[i].x!=Lis[i-1].x)tx++;
117             int ID=Lis[i].id;
118             if(!st[ID].x1){
119                 st[ID].x1=tx;
120             }else{
121                 st[ID].x2=tx;
122             }
123         }
124
125         For(i,1,tx+1){                  //初始化vector
126             ads[i].clear();
127             mns[i].clear();
128         }
129
130         For(i,1,n){
131             int lx,rx;          //把星星挂到相应的横坐标上
132             lx=st[i].x1;        //ads为加, mns为减
133             rx=st[i].x2+1;
134             ads[lx].push_back(i);
135             mns[rx].push_back(i);
136         }
137         For(i,1,tx){
138             int sz;
139
140             sz=mns[i].size();
141             For(j,0,sz-1){           //先减后加
142                 int ID=mns[i][j];
143                 int lx,rx;
144                 lx=st[ID].y2;
145                 rx=st[ID].y1;
146                 Ins(1,1,ty,lx,rx,-st[ID].lgt);
147
148             }
149
150             sz=ads[i].size();
151             For(j,0,sz-1){
152                 int ID=ads[i][j];
153                 int lx,rx;
154                 lx=st[ID].y2;
155                 rx=st[ID].y1;
156                 Ins(1,1,ty,lx,rx,st[ID].lgt);
157             }
158             ans=max(ans,mx[1]);
159         }
160         write(ans); Pn;
161     }
162     return 0;
163 }

原文地址:https://www.cnblogs.com/HLAUV/p/9931979.html

时间: 2024-08-03 11:04:39

【Luogu P1502】 窗口的星星的相关文章

luogu P1502 窗口的星星

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

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

题目链接:https://www.luogu.org/problem/P1502 P1502 窗口的星星 提交2.78k 通过682 时间限制1.00s 内存限制125.00MB 提交代码加入收藏 题目提供者cyrcyr 难度省选/NOI- 历史分数100 提交记录查看题解 标签 高性能高级数据结构 查看算法标签 相关讨论 进入讨论版 查看讨论 推荐题目 查看推荐 展开 题目背景 小卡买到了一套新房子,他十分的高兴,在房间里转来转去. 题目描述 晚上,小卡从阳台望出去,“哇~~~~好多星星啊”,

Java窗口--漫天星星

学习Java过程中,在树上遇到的一个例子,我将这个例子“进化”了.具体向下看: 编写工具:EditPlus JDK版本: 1.7.0_60-b19 要求:输出 300 颗位置随机的星星 效果如下: 我认为花星星并不需要太过于复杂的绘制 大婶们,见了别笑小弟自恋哦~~~~ 思路: 1 . 创建一个 1024 x 768 的窗口 2 . 将画布添加到窗口上 (在窗口的重绘方法中花 300 颗星星) 3 . 显示窗口 1 import java.awt.*; //导入包涵窗口的类 2 3 public

Luogu1502 窗口的星星 (线段树扫描线)

将每个点拓展为矩形,将\(y\)离散,延\(x\)轴扫描,每次更新最值 用了一百年的pushdown操作疑似有问题,WA了一发,y数组没开够又RE了一发... 话说POJ上的情书让我回忆起童年那个彪悍的女孩,一晃十年了 Fleeting time does not blur my memory of you. Can it really be 4 years since I first saw you? I still remember, vividly, on the beautiful Zh

数据结构(下)

1.树套树 (1)树状数组套树状数组 前置: 树状数组如何支持区间加以及区间查询 维护一个差分数组,用来求一个位置的值,我们只需要把前缀和看作是一个矩形, 减去两数插值产生的贡献即可 1.P4514 上帝造题的七分钟 题意:支持矩形加某个数和矩形查询值. 考虑将每个点差分,当我们要将\((a,b),(x,y)\)范围内的矩阵加1时,其实就是: \(A(a,b)+1\) \(A(x,b+1)-1\) \(A(a,y+1)-1\) \(A(x+1,y+1)+1\) 于是乎,前缀和: \[\sum_{

luogu题解 UVA11536 【Smallest Sub-Array】最短set区间&amp;滑动窗口

题目链接: https://www.luogu.org/problemnew/show/UVA11536 题目大意: 给定一个\(N,M,K\),构造这样的数列: \(x[1]=1,x[2]=2,x[3]=3\) \(x[i]=(x[i-1]+x[i-2]+x[i-3])\mod M+1(N>=i>=4)\) 然后问你是否存在一个在\(x[1]\)到\(x[n]\)中的区间,使得\([1,K]\)所有元素在其中至少出现过一次. 若存在,输出这个区间最短长度:否则输出\("sequen

luogu P1886滑动窗口

\(luogu\ P1886\)滑动窗口 题目链接 这道题目比较简单,但是因为经常忘记单调队列做滑动窗口所以写博客来加深一下印象. 如果求区间最小值,我们用发现右端点从前往后扫的方法一个数如果有贡献,当且仅当当前扫描的右端点的前面到这个数中间没有比这个数更小的数,因为如果有比这个数更小的数的话,这个更小的数肯定就会成为区间的最小值.如果一个数没有贡献的时候就是区间的左端点比这个数的下标要大的时候. 所以我们用一个双端队列来维护,每次进入队列的时候检查队尾的数如果比要加入得数大的话,就不断弹出队尾

luogu P1886 滑动窗口

题目描述 现在有一堆数字共N个数字(N<=10^6),以及一个大小为k的窗口.现在这个从左边开始向右滑动,每次滑动一个单位,求出每次滑动后窗口中的最大值和最小值. 例如: The array is [1 3 -1 -3 5 3 6 7], and k = 3. 输入输出格式 输入格式: 输入一共有两行,第一行为n,k. 第二行为n个数(<INT_MAX). 输出格式: 输出共两行,第一行为每次窗口滑动的最小值 第二行为每次窗口滑动的最大值 输入输出样例 输入样例#1: 8 3 1 3 -1 -

滑动窗口(luogu 1886)

题目描述 现在有一堆数字共N个数字(N<=10^6),以及一个大小为k的窗口.现在这个从左边开始向右滑动,每次滑动一个单位,求出每次滑动后窗口中的最大值和最小值. 例如: The array is [1 3 -1 -3 5 3 6 7], and k = 3. 输入输出格式 输入格式: 输入一共有两行,第一行为n,k. 第二行为n个数(<INT_MAX). 输出格式: 输出共两行,第一行为每次窗口滑动的最小值 第二行为每次窗口滑动的最大值 输入输出样例 输入样例 8 3 1 3 -1 -3 5