Luogu1502 窗口的星星

40 篇文章 0 订阅
5 篇文章 0 订阅

原题链接:https://www.luogu.org/problemnew/show/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

说明

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

题解

一个基础的扫描线,这里先讲讲扫描线:

在这道题中,我们可以以每个星星为左下角,扩展出一个和窗户一样的矩形,以样例中第二组数据为例,扩展后的矩形如图,左下角为点的编号:

这时,我们再建立出扫描线:

如图,我们以每个矩形的上下底建立扫描线,下底所在直线的权值为那个星星的亮度,上底所在直线的权值为星星亮度的相反数。每次我们从下往上枚举每条扫描线,在扫描线覆盖的区间内加上线的权值,每次统计一下区间中的最大值更新答案就好了。

针对区间加的操作,我们可以离散化横坐标,建一棵线段树来进行区间加以及维护最大值。

代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int M=100005;
struct pt{
    ll x,y,val;
};
struct li{
    ll le,ri,val,h;
};
struct node{
    ll maxn,add;
};
node tree[M<<2];
bool cmp1(pt a,pt b){return a.x==b.x?a.y<b.y:a.x<b.x;}
bool cmp2(li a,li b){return a.h==b.h?a.val>b.val:a.h<b.h;}
pt star[M],x[M];
li line[M];
int n,h,w,tot;
void in()
{
    memset(star,0,sizeof(star));
    memset(line,0,sizeof(line));
    memset(x,0,sizeof(x));
    memset(tree,0,sizeof(tree));
    scanf("%d%d%d",&n,&w,&h);
    for(int i=1;i<=n;++i)
    {
        scanf("%lld%lld%lld",&star[i].x,&star[i].y,&star[i].val);
        x[i].x=star[i].x;x[i].val=i;
        x[i+n].x=star[i].x+w-1;x[i+n].val=i;
    }
}
void lisan()
{
    int totx=2*n,k=0;
    sort(x+1,x+1+totx,cmp1);
    x[0].x=-1;
    for(int i=1;i<=totx;++i)
    {
        if(x[i].x!=x[i-1].x)++k;
        if(!line[x[i].val].le)line[x[i].val].le=k,line[x[i].val].h=star[x[i].val].y,
        line[x[i].val].val=star[x[i].val].val;
        else line[x[i].val].ri=k;
    }
    for(int i=1;i<=n;++i)
    {
        line[i+n]=line[i];
        line[i+n].val=-line[i].val;
        line[i+n].h=line[i].h+h-1;
    }
    sort(line+1,line+1+totx,cmp2);
}
void up(int v)
{tree[v].maxn=max(tree[v<<1].maxn,tree[v<<1|1].maxn);}
void push(int v)
{
    tree[v<<1].maxn+=tree[v].add;
    tree[v<<1|1].maxn+=tree[v].add;
    tree[v<<1].add+=tree[v].add;
    tree[v<<1|1].add+=tree[v].add;
    tree[v].add=0;
}
void add(int v,int lb,int rb,int le,int ri,int ad)
{
    if(lb<=le&&ri<=rb)
    {
        tree[v].add+=ad;
        tree[v].maxn+=ad;
        return;
    }
    if(tree[v].add)push(v);
    int mid=(le+ri)>>1;
    if(lb<=mid)add(v<<1,lb,rb,le,mid,ad);
    if(mid<rb)add(v<<1|1,lb,rb,mid+1,ri,ad);
    up(v);
}
void ac()
{
    lisan();
    ll ans=-1;
    int totx=2*n;
    for(int i=1;i<=totx;++i)
    {
        add(1,line[i].le,line[i].ri,1,totx,line[i].val);
        ans=max(ans,tree[1].maxn);
    }
    printf("%lld\n",ans);
}
int main()
{
    int T;
    scanf("%d",&T);
    for(int i=1;i<=T;++i)
    in(),ac();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShadyPi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值