扫描线模板(矩形面积并与周长并)

9102qyy / 2024-10-07 / 原文

扫描线模板(矩形面积并)

首先离散化的思想,将各个线段细分到单位长,于是就是动态求当前值域内 tag \(> 1\) 的数量。

以下是参考代码,十分优美

int n, cnt;
ll xx[N];
struct Scanline{
    ll y;
    ll lx, rx;
    int inout;
    bool operator < (const Scanline &t) const{
        return y < t.y;
    }
}line[N];
void lineadd(ll yt, ll lxt, ll rxt, int inoutt, int count){
    line[count].inout = inoutt;
    line[count].y = yt;
    line[count].lx = lxt;
    line[count].rx = rxt;
}
int ls(int p){return p << 1;}
int rs(int p){return p << 1|1;}
struct SegmentTree{
    ll tree[N << 2|1];
    int tag[N << 2 |1];
    inline void push_up(int p, int pl, int pr){
        // 向上传递节点,更新节点值
        if(tag[p]){
            tree[p] = xx[pr] - xx[pl];
        }else if(pl + 1 == pr){
            tree[p] = 0;
        }else{
            tree[p] = tree[ls(p)] + tree[rs(p)];
        }
    }

    inline void update(int L, int R, int p, int pl, int pr, int inoutt){
        if(L <= pl && pr <= R){
            tag[p] += inoutt;
            push_up(p, pl, pr);
            return ;
        }
        if(pl + 1 == pr) return ;
        int mid = (pl + pr) >> 1;
        if(L <= mid){
            update(L, R, ls(p), pl, mid, inoutt);
        }
        if(R >= mid + 1){
            update(L, R, rs(p), mid, pr, inoutt); // 注意不是 mid + 1,因为连续性
        }
        push_up(p, pl, pr);
    }

};
SegmentTree seg;

void solve() {
    cin >> n;
    ll x1, x2, y1, y2;
    for(int i = 1; i <= n; i++){
        cin >> x1 >> y1 >> x2 >> y2;
        lineadd(y1, x1, x2, 1, ++cnt);
        xx[cnt] = x1;
        lineadd(y2, x1, x2, -1, ++cnt);
        xx[cnt] = x2;
    }
    sort(xx + 1, xx + 1 + cnt);
    sort(line + 1, line + 1 + cnt);
    int xlen = unique(xx + 1, xx + 1 + cnt) - (xx + 1);
    ll ans = 0;
    for(int i = 1; i <= cnt; i++){
        ans += seg.tree[1]*(line[i].y - line[i - 1].y);
        int L = lower_bound(xx + 1, xx + 1 + xlen, line[i].lx) - xx;
        int R = lower_bound(xx + 1, xx + 1 + xlen, line[i].rx) - xx;
        seg.update(L, R, 1, 1, xlen, line[i].inout);
    }
    cout << ans << "\n";
}

矩形周长并复杂一些,并且存在一个细节 - 对于高度相同线的先增后删,避免两条边重合
思路是分成竖边和横边,分别计算,每条边的对答案的贡献是这一条边加上后的变化值(tag &>1& 的数量)
以下是参考代码:

// Created by qyy on 2024/10/6.

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

#define PII pair<int, int>
#define endl "\n"

const long long inf = 0x3f3f3f3f3f3f3f3f;
const int N = 2e5 + 10;
const int mod = 7;

int n, cnt, cnt2;
ll xx[N], xx2[N];
struct Scanline{
    ll y;
    ll lx, rx;
    int inout;
}line[N], line2[N];
bool cmp(Scanline a, Scanline b){
    if(a.y != b.y){
        return a.y < b.y;
    }else{
        return a.inout > b.inout;
    }
}
void lineadd(ll yt, ll lxt, ll rxt, int inoutt, int count){
    line[count].inout = inoutt;
    line[count].y = yt;
    line[count].lx = lxt;
    line[count].rx = rxt;
}
void lineadd2(ll yt, ll lxt, ll rxt, int inoutt, int count){
    line2[count].inout = inoutt;
    line2[count].y = yt;
    line2[count].lx = lxt;
    line2[count].rx = rxt;
}
int ls(int p){return p << 1;}
int rs(int p){return p << 1|1;}
struct SegmentTree{
    ll tree[N << 2|1];
    int tag[N << 2 |1];
    inline void push_up(int p, int pl, int pr){
        // 向上传递节点,更新节点值
        if(tag[p]){
            tree[p] = xx[pr] - xx[pl];
        }else if(pl + 1 == pr){
            tree[p] = 0;
        }else{
            tree[p] = tree[ls(p)] + tree[rs(p)];
        }
    }

    inline void update(int L, int R, int p, int pl, int pr, int inoutt){
        if(L <= pl && pr <= R){
            tag[p] += inoutt;
            push_up(p, pl, pr);
            return ;
        }
        if(pl + 1 == pr) return ;
        int mid = (pl + pr) >> 1;
        if(L <= mid){
            update(L, R, ls(p), pl, mid, inoutt);
        }
        if(R >= mid + 1){
            update(L, R, rs(p), mid, pr, inoutt); // 注意不是 mid + 1,因为连续性
        }
        push_up(p, pl, pr);
    }

};
SegmentTree seg;

void solve() {
    cin >> n;
    ll x1, x2, y1, y2;
    for(int i = 1; i <= n; i++){
        cin >> x1 >> y1 >> x2 >> y2;
        lineadd(y1, x1, x2, 1, ++cnt);
        xx[cnt] = x1;
        lineadd(y2, x1, x2, -1, ++cnt);
        xx[cnt] = x2;

        lineadd2(x1, y1, y2, 1, ++cnt2);
        xx2[cnt2] = y1;
        lineadd2(x2, y1, y2, -1, ++cnt2);
        xx2[cnt2] = y2;
    }
    sort(xx + 1, xx + 1 + cnt);
    sort(line + 1, line + 1 + cnt, cmp);

    int xlen = unique(xx + 1, xx + 1 + cnt) - (xx + 1);
    ll ans = 0;
    ll last = 0;
    for(int i = 1; i <= cnt; i++){
        int L = lower_bound(xx + 1, xx + 1 + xlen, line[i].lx) - xx;
        int R = lower_bound(xx + 1, xx + 1 + xlen, line[i].rx) - xx;
        last = seg.tree[1];
        seg.update(L, R, 1, 1, xlen, line[i].inout);
        ans += abs(seg.tree[1] - last);
    }

    for(int i = 1; i <= cnt; i++){
        line[i] = line2[i];
        xx[i] = xx2[i];
    }
    memset(seg.tree, 0, sizeof(seg.tree));
    memset(seg.tag, 0, sizeof(seg.tag));

    sort(xx + 1, xx + 1 + cnt);
    sort(line + 1, line + 1 + cnt, cmp);
    xlen = unique(xx + 1, xx + 1 + cnt) - (xx + 1);
    for(int i = 1; i <= cnt; i++){
        int L = lower_bound(xx + 1, xx + 1 + xlen, line[i].lx) - xx;
        int R = lower_bound(xx + 1, xx + 1 + xlen, line[i].rx) - xx;
        last = seg.tree[1];
        seg.update(L, R, 1, 1, xlen, line[i].inout);
        ans += abs(seg.tree[1] - last);
    }
    cout << ans << "\n";
}


signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    int t = 1;
    //cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}