【ACM-ICPC】Nowcoder Summer Training Camp 6 TH Hopping Rabbit

题意

二维平面上,有n个已知矩形,我们现需选择一个点,以该点为中心,d(已知)为步长,上下左右扩展形成的点网中的任意一点不能落到任意矩形中。

解法分析

因为步长为d,只要从(0, 0)-(d-1, d-1)中找点。
以下长表示x坐标长度,宽表示y坐标长度
将矩形分为三种类型:
1.长宽均大于等于d,输出为no。
2.长或者宽只有一个小于d,则只对我们选点的y或者x坐标有一个限定。
3.长宽小于d,则在(0,0)-(d-1,d-1)对应的矩形我们是不能取的。
问题转化为了我有一个大矩形区域,并且有n个矩形覆盖,找出区域中哪个点没被覆盖?
矩形覆盖的并,用扫描线可以解决。

代码

#include <bits/stdc++.h>
using namespace std;
struct seg
{
    int l, r, v;
};
vector<seg> q[100010];
struct segtree
{
    int minn;
};
struct node
{
    int minn, min_id;
    int lazy;
} tree[400010];
void build(int x, int l, int r)
{
    if (l == r)
    {
        tree[x] = {0, l, 0};
        return;
    }
    int mid = (l + r) >> 1;
    build(x << 1, l, mid);
    build(x << 1 | 1, mid + 1, r);
    tree[x] = {0, l, 0};
}
void push_up(int x)
{
    if (tree[x << 1].minn < tree[x << 1 | 1].minn)
        tree[x].minn = tree[x << 1].minn,
        tree[x].min_id = tree[x << 1].min_id;
    else
        tree[x].minn = tree[x << 1 | 1].minn,
        tree[x].min_id = tree[x << 1 | 1].min_id;
}
void push_down(int x)
{
    if (!tree[x].lazy)
        return;
    tree[x << 1].minn += tree[x].lazy;
    tree[x << 1].lazy += tree[x].lazy;
    tree[x << 1 | 1].minn += tree[x].lazy;
    tree[x << 1 | 1].lazy += tree[x].lazy;
    tree[x].lazy = 0;
}
void change(int x, int l, int r, int ql, int qr, int v)
{
    // cout << l << ' ' << r << ' ' << ql << ' ' << qr << endl;
    if (ql <= l && r <= qr)
    {
        tree[x].lazy += v;
        tree[x].minn += v;
        return;
    }
    push_down(x);
    int mid = (l + r) >> 1;
    if (mid >= ql)
        change(x << 1, l, mid, ql, qr, v);
    if (mid < qr)
        change(x << 1 | 1, mid + 1, r, ql, qr, v);
    push_up(x);
}
int main()
{
    int n, d;
    cin >> n >> d;
    bool flag = 0;
    for (int i = 1; i <= n; ++i)
    {
        int x1, y1, x2, y2;
        cin >> x1 >> y1 >> x2 >> y2;
        // cout << x1 <<' ' << y1 << ' ' << x2 << ' ' << y2<< endl;
        if (x2 - x1 >= d && y2 - y1 >= d)
            flag = 1;
        else if (x2 - x1 >= d)
        {
            y1 = (y1 % d + d) % d;
            y2 = ((y2 - 1) % d + d) % d;
            if (y1 <= y2)
                q[0].push_back({y1, y2, 1});
            else
                q[0].push_back({y1, d - 1, 1}),
                    q[0].push_back({0, y2, 1});
        }
        else if (y2 - y1 >= d)
        {
            x1 = (x1 % d + d) % d;
            x2 = (x2 % d + d - 1) % d + 1;
            if (x1 < x2)
                q[x1].push_back({0, d - 1, 1}),
                    q[x2].push_back({0, d - 1, -1});
            else
                q[x1].push_back({0, d - 1, 1}),
                    q[0].push_back({0, d - 1, 1}),
                    q[x2].push_back({0, d - 1, -1});
        }
        else
        {
            x1 = (x1 % d + d) % d;
            x2 = (x2 % d + d - 1) % d + 1;
            y1 = (y1 % d + d) % d;
            y2 = ((y2 - 1) % d + d) % d;
            if (x1 < x2 && y1 <= y2)
            {
                q[x1].push_back({y1, y2, 1});
                q[x2].push_back({y1, y2, -1});
            }
            else if (x1 < x2)
            {
                q[x1].push_back({y1, d - 1, 1});
                q[x1].push_back({0, y2, 1});
                q[x2].push_back({y1, d - 1, -1});
                q[x2].push_back({0, y2, -1});
            }
            else if (y1 <= y2)
            {
                q[x1].push_back({y1, y2, 1});
                q[0].push_back({y1, y2, 1});
                q[x2].push_back({y1, y2, -1});
            }
            else
            {
                q[x1].push_back({y1, d - 1, 1});
                q[x1].push_back({0, y2, 1});
                q[0].push_back({y1, d - 1, 1});
                q[0].push_back({0, y2, 1});
                q[x2].push_back({y1, d - 1, -1});
                q[x2].push_back({0, y2, -1});
            }
        }
        // cout << x1 <<' ' << y1 << ' ' << x2 << ' ' << y2<< endl;
    }
    if (flag)
        cout << "NO";
    else
    {
        build(1, 0, d - 1);
        // cout << "1";
        for (int i = 0; i <= d - 1; ++i)
        {
            for (auto j : q[i])
                change(1, 0, d - 1, j.l, j.r, j.v);
                    // cout << i << ' ' << j.l << ' ' << j.r << ' ' << j.v << endl;
            // cout << "1";
            if (!tree[1].minn)
            {
                cout << "YES" << endl
                     << i << ' ' << tree[1].min_id;
                flag = 1;
                break;
            }
        }
        if (!flag)
            cout << "NO";
    }
    return 0;
}

扫描线算法

二维平面上求矩形面积并。
先考虑暴力的情况,对于一个矩形覆盖,直接x,y枚举覆盖。
稍微不暴力一点,对x枚举,每个x建立线段树,进行区间修改,然后统计。
每个x建立线段树果然还是有点凶残,能不能只建一个线段树然后统计呢?
当然可以。
考虑到之前我们是对x枚举修改y区间,但如果我们对x差分,只在矩形左边右边修改区间。然后对x从头到尾扫描,修改线段树,并统计答案,这就大大优化了时间和空间。
这就是扫描线算法。

小结

线段树不熟练,代码力不够,导致这题想出解法但比赛中没过。这题细节还是挺多的。先挖个坑,复习线段树。

上一篇:Rabbit发送接收消息


下一篇:一文看懂Spring Boot整合Rabbit MQ实现多种模式的生产和消费