51Nod - 1559 线段树 + 扫描线

题意:

题目链接:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1559
波雷卡普有一个n×m大小的棋盘,上面有k个车。他又放了q个矩形在上面。每一个矩形要受到保护。矩形受到保护的意思是对于该矩形内部所有的格子能够被这个矩形内的某个车攻击到或者被占据,和矩形外面的车无关,即矩形外面的车不能攻击到矩形里面。车的位置是固定的。
样例示意图如下:
这里写图片描述
题意有点坑,要注意这里判断矩形是否安全的时候,只要考虑当前要判断的矩形和所有的车,而不要考虑其他矩形的影响。


思路:

如果理解对了题意,可以很容易想到,一个矩形如果能称得上的是安全的,就等价于这个矩形的每一行都有车或者每一列都有车。
这里不妨先考虑每一列都有车,按照示意图,x是列,y是行。矩形左下角顶点为(lx,ly),右上角顶点为(rx,ry)。
这样对于一个矩形,判断每一列是否都有车,其实就相当于一个对于区间[lx,rx]查询,这就容易想到线段树,但是一列可能会有多个车,所以不能用线段树直接保存每个车的纵坐标y。
注意到这样一个事实:对于每个矩形,其实我们只想知道横坐标x在[lx,rx]之间的且纵坐标y比不超过矩形上横边ry的且与ry最接近的车,这样的车的y只要大于等于当前矩形的下横边ly,就说明这个车在矩形中。那么线段树就不需要保存所有的车的位置,只要维护当前小于等于ry的最大的y即可。
1. 将每个车都当作大小为1的矩形,这样把车和矩形放在一起按照ry从小到大排序。
2. 如果当前是车,就更新线段树,让x的位置的值更新为较大的y。
3. 如果当前是矩形,就查询线段树,判断线段树[lx,rx]中的最小值是否大于等于ly。
之所以不判断y和ry的关系是因为之前的排序,保证了当前的线段树中不会有y的值大于ry。


代码:

#include <bits/stdc++.h>
using namespace std;
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
const int MAXN = 4e5 + 10;
const int INF = 0x3f3f3f3f;

struct node {
    int lx, ly, rx, ry, id, type;
    bool operator < (const node &rhs) const {
        return ry < rhs.ry || (ry == rhs.ry && type > rhs.type);
    }
} a[MAXN];

struct SegmentTree {
    int C[MAXN << 2];

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

    void build(int l, int r, int rt) {
        C[rt] = 0;
        if (l == r) return;
        int m = (l + r) >> 1;
        build(lson); build(rson);
    }

    int query(int L, int R, int l, int r, int rt) {
        if (L <= l && r <= R) return C[rt];
        int m = (l + r) >> 1, res = INF;
        if (L <= m) res = min(res, query(L, R, lson));
        if (R > m) res = min(res, query(L, R, rson));
        return res;
    }

    void update(int pos, int val, int l, int r, int rt) {
        if (l == r) {
            C[rt] = max(C[rt], val);
            return;
        }
        int m = (l + r) >> 1;
        if (pos <= m) update(pos, val, lson);
        else update(pos, val, rson);
        push_up(rt);
    }

}segtree;

bool ans[MAXN];

void solve(int n, int cnt) {
    sort (a + 1, a + 1 + cnt);
    segtree.build(1, n, 1);
    for (int i = 1; i <= cnt; i++) {
        if (a[i].type == 1) {
            segtree.update(a[i].rx, a[i].ry, 1, n, 1);
        }
        else if (!ans[a[i].id]) {
           // cout << segtree.query(a[i].lx, a[i].rx, 1, n, 1) << endl;
            ans[a[i].id] = segtree.query(a[i].lx, a[i].rx, 1, n, 1) >= a[i].ly;
        }
    }
}

inline bool scan_d(int &num)
{
    char in;bool IsN=false;
    in=getchar();
    if(in==EOF) return false;
    while(in!='-'&&(in<'0'||in>'9')) in=getchar();
    if(in=='-'){ IsN=true;num=0;}
    else num=in-'0';
    while(in=getchar(),in>='0'&&in<='9'){
            num*=10,num+=in-'0';
    }
    if(IsN) num=-num;
    return true;
}

int main() {
    //freopen("in.txt", "r", stdin);
    int n, m, k, q;
    scan_d(n); scan_d(m); scan_d(k); scan_d(q);
    for (int i = 1; i <= k; i++) {
        scan_d(a[i].rx); scan_d(a[i].ry);
        a[i].type = 1;
    }
    for (int i = 1; i <= q; i++) {
        scan_d(a[i + k].lx); scan_d(a[i + k].ly);
        scan_d(a[i + k].rx); scan_d(a[i + k].ry);
        a[i + k].id = i; a[i + k].type = 0;
    }
    solve(n, k + q);
    for (int i = 1; i <= k + q; i++) {
        swap(a[i].lx, a[i].ly);
        swap(a[i].rx, a[i].ry);
    }
    solve(m, k + q);
    for (int i = 1; i <= q; i++) {
        if (ans[i]) puts("YES");
        else puts("NO");
    }
    return 0;
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值