线段树 poj2482 Stars in Your Window

不得不佩服出题的,,这篇情书写的真是真情实感啊→_→

→_→然而突然笔锋一转那么问题来了人都吓蠢。


感觉这题出的非常好,也是一个十分经典的一个类型。

就是如何选取一个长和宽是确定的矩形使里面的权值最大


我们想到扫描线的时候,之前做的题目都是有线段的,但是这里好像找不到扫描的线段了,一下子不知道从何下手

如果把这题稍微转换一下,,题目就会变得非常简单..

假如点P(x,y),矩形的高和宽分别是H和W

那么我就插入区间[x,x+W-1]。

这样插入表示的意义是,如果矩形的右端点落在[x,x+W-1]内,那么对于点P一定包含在矩形内部

所以,题目转换成,添加许多个区间以后,哪个点被线段覆盖的次数最多。

此时又变成我们最熟悉最经典的扫描线了~


有一个地方需要注意,那就是高度,如果超过了H,就需要把一开始的区间删除,直到新增加区间以后高度能<=H才行

所以我们可以用双端队列去维护矩形中线段的编号,以方便删除最开始的区间

#include<map>
#include<set>
#include<cmath>
#include<stack>
#include<deque>
#include<queue>
#include<cstdio>
#include<string>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<functional>

using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define root 1,rear,1

int const MX = 4e4 + 5;

int rear;
deque<int>work;
LL A[MX], WL[MX], WR[MX];
int MAX[MX << 2], col[MX << 2];

struct Que {
    int d;
    int top, L, R;
    Que() {}
    Que(int _top, int _L, int _R, int _d) {
        top = _top; L = _L; R = _R; d = _d;
    }
    bool operator<(const Que &b)const {
        return top < b.top;
    }
} Q[MX];

int BS(LL x) {
    int L = 1, R = rear, m;
    while(L <= R) {
        m = (L + R) >> 1;
        if(A[m] == x) return m;
        if(A[m] > x) R = m - 1;
        else L = m + 1;
    }
    return -1;
}

void push_up(int rt) {
    MAX[rt] = max(MAX[rt << 1], MAX[rt << 1 | 1]);
}

void push_down(int rt) {
    if(col[rt]) {
        col[rt << 1] += col[rt];
        col[rt << 1 | 1] += col[rt];
        MAX[rt << 1] += col[rt];
        MAX[rt << 1 | 1] += col[rt];
        col[rt] = 0;
    }
}

void update(int L, int R, int d, int l, int r, int rt) {
    if(L <= l && r <= R) {
        col[rt] += d;
        MAX[rt] += d;
        return;
    }

    int m = (l + r) >> 1;
    push_down(rt);
    if(L <= m) update(L, R, d, lson);
    if(R > m) update(L, R, d, rson);
    push_up(rt);
}

int main() {
    int n, W, H;
    //freopen("input.txt", "r", stdin);
    while(~scanf("%d%d%d", &n, &W, &H)) {
        rear = 0;
        work.clear();
        memset(MAX, 0, sizeof(MAX));
        memset(col, 0, sizeof(col));

        for(int i = 1; i <= n; i++) {
            int d, y;
            scanf("%I64d%I64d%d", &WL[i], &y, &d);
            WR[i] = WL[i] + W - 1;
            Q[i] = Que(y, 0, 0, d);
            A[++rear] = WL[i];
            A[++rear] = WR[i];
        }
        sort(A + 1, A + 1 + rear);
        rear = unique(A + 1, A + 1 + rear) - A - 1;

        for(int i = 1; i <= n; i++) {
            Q[i].L = BS(WL[i]);
            Q[i].R = BS(WR[i]);
        }
        sort(Q + 1, Q + 1 + n);

        int ans = 0;
        for(int i = 1; i <= n; i++) {
            while(!work.empty() && Q[i].top - Q[work.back()].top >= H) {
                int id = work.back();
                work.pop_back();
                update(Q[id].L, Q[id].R, -Q[id].d, root);
            }
            work.push_front(i);
            update(Q[i].L, Q[i].R, Q[i].d, root);
            ans = max(ans, MAX[1]);
        }
        printf("%d\n", ans);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值