矩形面积并
2025年6月15日大约 6 分钟
矩形面积并
| 题目 | 难度 | |
|---|---|---|
| 850. 矩形面积 II | 困难 | |
| 3454. 分割正方形 II | 困难 | |
| P5490 【模板】扫描线 & 矩形面积并 | 
需要用到扫描线 & 特殊线段树
850. 矩形面积 II
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
public class Solution850 {
    private static final int MOD = (int) (1e9 + 7);
    public int rectangleArea(int[][] rectangles) {
        int m = 0;
        TreeMap<Integer, Integer> mp = new TreeMap<>();
//        for (int[] sq : squares) {
//            mp.put(sq[0], 1);
//            mp.put(sq[0] + sq[2], 1);
//        }
        for (int[] re : rectangles) {
            mp.put(re[0], 1);
            mp.put(re[2], 1);
        }
        for (Map.Entry<Integer, Integer> p : mp.entrySet()) p.setValue(m++);
        int[] A = new int[m];
        for (Map.Entry<Integer, Integer> p : mp.entrySet()) A[p.getValue()] = p.getKey();
        // 离散化结束
        // 把正方形的上下边界取出来
        List<int[]> vec = new ArrayList<>();
//        for (int[] sq : squares) {
//            vec.add(new int[]{sq[1], mp.get(sq[0]) + 1, mp.get(sq[0] + sq[2]), 1});
//            vec.add(new int[]{sq[1] + sq[2], mp.get(sq[0]) + 1, mp.get(sq[0] + sq[2]), -1});
//        }
        for (int[] re : rectangles) {
            vec.add(new int[]{re[1], mp.get(re[0]) + 1, mp.get(re[2]), 1});
            vec.add(new int[]{re[3], mp.get(re[0]) + 1, mp.get(re[2]), -1});
        }
        vec.sort(Arrays::compare);
        // 求总的面积并
        long tot = 0;
        LazyInfoSegmentTree seg = new LazyInfoSegmentTree(m);
        seg.build(A, 1, 1, m - 1);
        for (int i = 0; i + 1 < vec.size(); i++) {
            // 考虑水平线 y = vec[i][0] 和 y = vec[i + 1][0] 之间的情况
            seg.modify(1, 1, m - 1, vec.get(i)[1], vec.get(i)[2], vec.get(i)[3]);
            // 求横截长度
            int len = A[m - 1] - A[0];
            // 如果最小覆盖数是 0,那么扣掉相应的长度
            if (seg.info[1].mn == 0) len -= seg.info[1].len;
            // 面积 = 横截长度 * 高度差
            tot += (long) len * (vec.get(i + 1)[0] - vec.get(i)[0]);
        }
        return (int) (tot % MOD);
    }
    // 线段树模板,只需要实现 mergeInfo 和 _do,其余都是固定的
    static class LazyInfoSegmentTree {
        static class Info {
            // mn:当前节点的最小覆盖数
            // len:满足覆盖数 = 最小覆盖数的 A[i] 之和
            // lazy:加法的懒标记
            int mn, len, lazy;
            public Info(int mn, int len, int lazy) {
                this.mn = mn;
                this.len = len;
                this.lazy = lazy;
            }
        }
        Info mergeInfo(Info a, Info b) {
            int mn = Math.min(a.mn, b.mn);
            return new Info(mn, (a.mn == mn ? a.len : 0) + (b.mn == mn ? b.len : 0), 0);
        }
        // 对节点的覆盖数整个增加 qv,只影响 mn,不影响 len
        void _do(int p, int qv) {
            info[p].mn += qv;
            info[p].lazy += qv;
        }
        int n;
        Info[] info;
        public LazyInfoSegmentTree(int n) {
            this.n = n;
            info = new Info[4 * n];
            Arrays.fill(info, new Info(0, 0, 0));
        }
        void build(int[] A, int p, int l, int r) {
            if (l == r) {
                info[p] = new Info(0, A[r] - A[r - 1], 0);
                return;
            }
            int m = (l + r) >> 1;
            build(A, p << 1, l, m);
            build(A, p << 1 | 1, m + 1, r);
            maintain(p);
        }
        void maintain(int p) {
            info[p] = mergeInfo(info[p << 1], info[p << 1 | 1]);
        }
        void spread(int p) {
            if (info[p].lazy == 0) return;
            _do(p << 1, info[p].lazy);
            _do(p << 1 | 1, info[p].lazy);
            info[p].lazy = 0;
        }
        void modify(int p, int l, int r, int ql, int qr, int qv) {
            if (ql <= l && r <= qr) {
                _do(p, qv);
                return;
            }
            spread(p);
            int m = (l + r) >> 1;
            if (ql <= m) modify(p << 1, l, m, ql, qr, qv);
            if (qr > m) modify(p << 1 | 1, m + 1, r, ql, qr, qv);
            maintain(p);
        }
    }
}3454. 分割正方形 II
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
public class Solution3454 {
    // 线段树模板,只需要实现 mergeInfo 和 _do,其余都是固定的
    static class LazyInfoSegmentTree {
        static class Info {
            // mn:当前节点的最小覆盖数
            // len:满足覆盖数 = 最小覆盖数的 A[i] 之和
            // lazy:加法的懒标记
            int mn, len, lazy;
            public Info(int mn, int len, int lazy) {
                this.mn = mn;
                this.len = len;
                this.lazy = lazy;
            }
        }
        Info mergeInfo(Info a, Info b) {
            int mn = Math.min(a.mn, b.mn);
            return new Info(mn, (a.mn == mn ? a.len : 0) + (b.mn == mn ? b.len : 0), 0);
        }
        // 对节点的覆盖数整个增加 qv,只影响 mn,不影响 len
        void _do(int p, int qv) {
            info[p].mn += qv;
            info[p].lazy += qv;
        }
        int n;
        Info[] info;
        public LazyInfoSegmentTree(int n) {
            this.n = n;
            info = new Info[4 * n];
        }
        void build(int[] A, int p, int l, int r) {
            if (l == r) {
                info[p] = new Info(0, A[r] - A[r - 1], 0);
                return;
            }
            int m = (l + r) >> 1;
            build(A, p << 1, l, m);
            build(A, p << 1 | 1, m + 1, r);
            maintain(p);
        }
        void maintain(int p) {
            info[p] = mergeInfo(info[p << 1], info[p << 1 | 1]);
        }
        void spread(int p) {
            if (info[p].lazy == 0) return;
            _do(p << 1, info[p].lazy);
            _do(p << 1 | 1, info[p].lazy);
            info[p].lazy = 0;
        }
        void modify(int p, int l, int r, int ql, int qr, int qv) {
            if (ql <= l && r <= qr) {
                _do(p, qv);
                return;
            }
            spread(p);
            int m = (l + r) >> 1;
            if (ql <= m) modify(p << 1, l, m, ql, qr, qv);
            if (qr > m) modify(p << 1 | 1, m + 1, r, ql, qr, qv);
            maintain(p);
        }
    }
    public double separateSquares(int[][] squares) {
        int m = 0;
        TreeMap<Integer, Integer> mp = new TreeMap<>();
        for (int[] sq : squares) {
            mp.put(sq[0], 1);
            mp.put(sq[0] + sq[2], 1);
        }
        for (Map.Entry<Integer, Integer> p : mp.entrySet()) p.setValue(m++);
        int[] A = new int[m];
        for (Map.Entry<Integer, Integer> p : mp.entrySet()) A[p.getValue()] = p.getKey();
        // 离散化结束
        // 把正方形的上下边界取出来
        List<int[]> vec = new ArrayList<>();
        for (int[] sq : squares) {
            vec.add(new int[]{sq[1], mp.get(sq[0]) + 1, mp.get(sq[0] + sq[2]), 1});
            vec.add(new int[]{sq[1] + sq[2], mp.get(sq[0]) + 1, mp.get(sq[0] + sq[2]), -1});
        }
        vec.sort(Arrays::compare);
        // 求总的面积并
        long tot = 0;
        LazyInfoSegmentTree seg = new LazyInfoSegmentTree(m);
        seg.build(A, 1, 1, m - 1);
        for (int i = 0; i + 1 < vec.size(); i++) {
            // 考虑水平线 y = vec[i][0] 和 y = vec[i + 1][0] 之间的情况
            seg.modify(1, 1, m - 1, vec.get(i)[1], vec.get(i)[2], vec.get(i)[3]);
            // 求横截长度
            int len = A[m - 1] - A[0];
            // 如果最小覆盖数是 0,那么扣掉相应的长度
            if (seg.info[1].mn == 0) len -= seg.info[1].len;
            // 面积 = 横截长度 * 高度差
            tot += (long) len * (vec.get(i + 1)[0] - vec.get(i)[0]);
        }
        long now = 0;
        seg.build(A, 1, 1, m - 1);
        for (int i = 0; i + 1 < vec.size(); i++) {
            seg.modify(1, 1, m - 1, vec.get(i)[1], vec.get(i)[2], vec.get(i)[3]);
            int len = A[m - 1] - A[0];
            if (seg.info[1].mn == 0) len -= seg.info[1].len;
            now += (long) len * (vec.get(i + 1)[0] - vec.get(i)[0]);
            // delta 非负了,套公式
            long det = now - (tot - now);
            if (det >= 0) return vec.get(i + 1)[0] - 0.5 * det / len;
        }
        return -1;
    }
}P5490 【模板】扫描线 & 矩形面积并
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template<class Info, class Tag>
struct LazySegmentTree {
    const int n;
    vector<Info> info;
    vector<Tag> tag;
    LazySegmentTree(int n) : n(n), info(4 << (int) log2(n)), tag(4 << (int) log2(n)) {
    }
    LazySegmentTree(vector<Info> init) : LazySegmentTree(init.size()) {
        function<void(int, int, int)> build = [&](int p, int l, int r) {
            if (l == r) {
                info[p] = init[l - 1];
                return;
            }
            int m = (l + r) / 2;
            build(2 * p, l, m);
            build(2 * p + 1, m + 1, r);
            pull(p);
        };
        build(1, 1, n);
    }
    void pull(int p) {
        info[p] = info[p * 2] + info[p * 2 + 1];
    }
    void apply(int p, const Tag &v) {
        info[p].apply(v);
        tag[p].apply(v);
    }
    void push(int p) {
        apply(2 * p, tag[p]);
        apply(2 * p + 1, tag[p]);
        tag[p] = Tag();
    }
    void rangeApply(int p, int l, int r, int ql, int qr, const Tag &v) {
        if (ql <= l && r <= qr) {
            apply(p, v);
            return;
        }
        int m = (l + r) / 2;
        push(p);
        if (ql <= m) rangeApply(2 * p, l, m, ql, qr, v);
        if (qr > m) rangeApply(2 * p + 1, m + 1, r, ql, qr, v);
        pull(p);
    }
    void rangeApply(int l, int r, const Tag &v) {
        rangeApply(1, 1, n, l, r, v);
    }
};
struct Tag {
    ll add;
    Tag(int _rev = 0) : add{_rev} {
    }
    void apply(const Tag &t) {
        add += t.add;
    }
};
struct Info {
    int mn, len;
    Info() : mn(0), len(0) {
    }
    Info(int v, int x) : mn(v), len(x) {
    }
    void apply(const Tag &t) {
        mn += t.add;
    }
};
Info operator+(Info a, Info b) {
    Info c;
    int mn = min(a.mn, b.mn);
    c.mn = mn;
    c.len = (a.mn == mn ? a.len : 0) + (b.mn == mn ? b.len : 0);
    return c;
}
void solve() {
    int n;
    cin >> n;
    vector<array<int, 4> > rectangles(n);
    for (auto &re: rectangles) {
        cin >> re[0] >> re[1] >> re[2] >> re[3];
    }
    int m = 0;
    map<int, int> mp;
    for (const auto &re: rectangles) mp[re[0]] = mp[re[2]] = 1;
    for (auto &p: mp) p.second = m++;
    int A[m];
    for (auto &p: mp) A[p.second] = p.first;
    // 把正方形的上下边界取出来
    vector<array<int, 4> > vec;
    for (auto &re: rectangles) {
        vec.push_back({re[1], mp[re[0]] + 1, mp[re[2]], 1});
        vec.push_back({re[3], mp[re[0]] + 1, mp[re[2]], -1});
    }
    sort(vec.begin(), vec.end());
    vector<Info> init(m - 1);
    for (int i = 1; i < m; ++i) {
        init[i - 1] = Info(0, A[i] - A[i - 1]);
    }
    LazySegmentTree<Info, Tag> seg(init);
    // 求总的面积并
    ll tot = 0;
    for (int i = 0; i + 1 < vec.size(); i++) {
        // 考虑水平线 y = vec[i][0] 和 y = vec[i + 1][0] 之间的情况
        seg.rangeApply(1, 1, m - 1, vec[i][1], vec[i][2], vec[i][3]);
        // 求横截长度
        int len = A[m - 1] - A[0];
        // 如果最小覆盖数是 0,那么扣掉相应的长度
        if (seg.info[1].mn == 0) len -= seg.info[1].len;
        // 面积 = 横截长度 * 高度差
        tot += 1LL * len * (vec[i + 1][0] - vec[i][0]);
    }
    cout << tot << endl;
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t = 1;
    //    cin >> t;
    while (t--) solve();
    return 0;
}(全文完)