[Luogu P1502] [POJ 2482] 窗口的星星

洛谷传送门

POJ传送门

POJ自带一篇唯美情书...

题目背景

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

题目描述

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

输入输出格式
输入格式:

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

对于每组数据

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

接下来n行,每行三个整数xi,yi,li 表示星星的坐标在(xi,yi),亮度为li。( 0<=xi,yi<231 0 <= x i , y i < 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

说明

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

解题分析

此题为本蒟蒻参考网上各位dalao讲解最后AC的第一道扫描线+线段树,自然要详细分析扫描线算法。(可能是大佬们太强了, 蒟蒻看题解都觉得有困难QAQ)

此类使用矩形去覆盖多个点的问题, 如果根据题意不断移动矩形再计算矩形内的点, 复杂度势必十分爆炸, 所以我们改变策略, 考虑从每个点来算起。

因为矩形有一个固定的长和宽, 所以我们可以将每个点可以被框入的范围视为一个矩形。 为了不重复计算 我们统一只计算向上及向右的矩形, 如图:

其中若有矩形相交即说明在那个区域可以找到一个矩形将有交集的点全部覆盖。

现在我们注意到, 因为横坐标范围很大,判断矩形是否相交显得十分困难,所以我们可以对x轴方向重新分配坐标, 如图:

我们可以发现,如此分配后矩形判断相交显得十分简单。例如对于以B点构建的矩形, 其下标在2-5之间, 所以顶点x坐标在2-5的所有矩形都可能与B点构建的矩形相交。既然有这样优秀的性质, 我们就可以利用分配的 x x 下标建立一棵线段树来记录区间信息。

接下来我们就需要考虑如何处理y轴方向上的矩形相交、 相离的问题。 因为我们已经构造出了n个矩形,可以利用矩形的上、下底的关系, 将上底的权值设为valx, 下底的权值设为 valx v a l x , 于是可以得到下面这幅图:

接下来我们将所有矩形的“上边”(其实是扩展出来的点)按照y坐标从小到大排列,再依次加入每条边, 用线段树维护该条边覆盖的点的权值。

对于查询最大亮度的操作,我们只需要在插入值为正值时在线段树上查询区间最大值, 即可得到当前矩形最大覆盖点的亮度和。

代码如下:

#include <cstdio>
#include <cctype>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
#define MX 100005
#define R register
#define IN inline
#define W while
#define gc getchar()
struct Stars
{
    int x, y, val, typ, id;
}star[MX], anti[MX];
int tot, cnt, maxans, num, height, width;
int lef[MX], rig[MX];
IN bool cmp_x (const Stars &x, const Stars &y)
{return x.x == y.x ? x.y < y.y : x.x < y.x;}
IN bool cmp_y (const Stars &x, const Stars &y)
{return x.y == y.y ? x.val > y.val : x.y < y.y;}
//感觉值小的应该放在前面, 但Luogu上面会WA一个点。存疑。
template <typename T>
IN void in (T &x)
{
    x = 0; R char c = gc;
    W (!isdigit(c)) c = gc;
    W (isdigit(c))
    {x = (x << 1) + (x << 3), x += c - 48, c = gc;}
}
namespace SGT
{
    #define ls tree[now].son[0]
    #define rs tree[now].son[1]
    struct Nodes
    {
        int val, son[2], lazy;
    }tree[MX << 1];
    IN void pushup(const int &now)
    {tree[now].val = max(tree[ls].val, tree[rs].val);}
    IN void pushdown(const int &now)
    {
        if(!tree[now].lazy) return;
        tree[ls].val += tree[now].lazy;
        tree[rs].val += tree[now].lazy;
        tree[ls].lazy += tree[now].lazy;
        tree[rs].lazy += tree[now].lazy;
        tree[now].lazy = 0;
    }
    IN void build(int now, int lef, int rig)
    {//注意多组数据要清空之前的线段树。
        tree[now].val = tree[now].lazy = 0;
        if(lef == rig) return;
        int mid = (lef + rig) >> 1;
        tree[now].son[0] = now << 1;
        tree[now].son[1] = now << 1 | 1;
        build(ls, lef, mid);
        build(rs, mid + 1, rig);
    }
    IN void add(int now, int lef, int rig, int lb, int rb, int delta)
    {
        if(!now) return;
        if(lb <= lef && rb >= rig)
        {
            tree[now].val +=  delta;
            tree[now].lazy += delta;
            return;
        }
        pushdown(now);
        int mid = (lef + rig) >> 1;
        if (mid >= lb) add(ls, lef, mid, lb, rb, delta);
        if (mid < rb) add(rs, mid + 1, rig, lb, rb, delta);
        pushup(now);
    }
    IN int query(int now, int lef, int rig, int lb, int rb)
    {
        if(lb <=lef && rb >= rig)
        {return tree[now].val;}
        pushdown(now);
        int mid = (lef + rig) >> 1;
        int rt = 0;
        if (mid >= lb) rt = max(rt, query(ls, lef, mid, lb, rb));
        if (mid < rb) rt = max(rt, query(rs, mid + 1, rig, lb, rb));
        return rt;
    }
}
using namespace SGT;
int main()
{
    int T;
    in(T);
    W (T--)
    {
        tot = cnt = maxans = 0;
        in(num), in(width), in(height);
        for (R int i = 1; i <= num; ++i)
        {
            in(star[i].x), in(star[i].y), in(star[i].val);
            star[i].id = i, star[i].typ = 0;
            anti[i].id = star[i].id, anti[i].y = star[i].y, anti[i].val = star[i].val;
        }
        tot = num;
        for (R int i = 1; i <= num; ++i)
        {//算出右边界
            star[++tot].x = star[i].x + width - 1;
            star[tot].typ = 1;
            star[tot].id = i;
        }
        sort(star + 1, star + 1 + tot, cmp_x);
        int cood;
        for (R int i = 1; i <= tot; ++i)
        {//离散化操作
            if(star[i].x == star[i - 1].x && i != 1) cood = cnt;
            else cood = ++cnt;
            if(star[i].typ) rig[star[i].id] = cood;
            else lef[star[i].id] = cood;
        }
        tot = num;
        for (R int i = 1; i <= num; ++i)
        {
            anti[++tot].y = anti[i].y + height - 1;
            anti[tot].id = anti[i].id;
            anti[tot].val = -anti[i].val;
        }//上边界
        sort(anti + 1, anti + 1 + tot, cmp_y);
        build(1, 1, cnt);
        int ans;
        for (R int i = 1;  i <= tot; ++i)
        {
            if(anti[i].val > 0)
            {
                ans = query(1, 1, cnt, lef[anti[i].id], rig[anti[i].id]);
                maxans = max(maxans, ans + anti[i].val);
            }
            add(1, 1, cnt, lef[anti[i].id], rig[anti[i].id], anti[i].val);
        }
        printf("%d\n", maxans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值