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

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

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

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

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 &>0& 的数量)
以下是参考代码:

// 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;
}

posted @   9102700  阅读(10)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
  1. 1 鼓楼 赵雷
  2. 2 我们的歌 王力宏
  3. 3 老街 李荣浩
  4. 4 周杰伦
  5. 5 可惜没如果 林俊杰
  6. 6 不将就 李荣浩
  7. 7 南方姑娘 赵雷
  8. 8 南方姑娘(弹唱版) 赵雷
  9. 9 如果可以 韦礼安
  10. 10 写给黄淮 邵帅
  11. 11 我想念 汪苏泷
  12. 12 雨天 汪苏泷
  13. 13 雨天雨天 汪苏泷
  14. 14 成都 赵雷
我们的歌 - 王力宏
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.

作词 : 王力宏/陈信延

作曲 : 王力宏

编曲 : 王力宏

已经听了一百遍

已经听了一百遍

怎么听都不会倦

从白天唱到黑夜

你一直在身边

如果世界太危险

只有音乐最安全

带着我进梦里面

让歌词都实现

无论是开心还是难过我的爱一直不变

不必担心时间流逝带走一切

无论是Hip-Hop 还是摇滚我的爱一直不变

所有美好回忆记录在里面

这种 Forever Love 那么深

我们的歌 那么真

无国界跨时代

再也不会叫我Kiss Goodbye

要每一句能够动人心弦 Yeah

情人总分分合合

可是我们却越爱越深

认识你 让我的幸福如此悦耳

能不能不要切歌

继续唱我们的歌​​

让感动一辈子都记得

已经听了一百遍

已经听了一百遍

怎么听都不会倦

从白天唱到黑夜

你一直在身边

如果世界太危险

只有音乐最安全

带着我进梦里面

让歌词都实现

无论是开心还是难过我的爱一直不变

不必担心时间流逝带走一切

无论是Hip-Hop 还是摇滚我的爱一直不变

所有美好回忆记录在里面

这种 Forever Love 那么深

我们的歌 那么真

无国界跨时代

再也不会叫我Kiss Goodbye

要每一句能够动人心弦 Yeah

情人总分分合合

可是我们却越爱越深

认识你 让我的幸福如此悦耳

能不能不要切歌

继续唱我们的歌​​

让感动一辈子都记得

情人总分分合合

情人总分分合合

可是我们却越爱越深

认识你 让我的幸福如此悦耳

能不能不要切歌

继续唱我们的歌​​

让感动一辈子都记得

电吉他/其他乐器:王力宏

鼓手:Eric Fawcrtt

贝斯:John Mumson

录音师/录音室:王力宏/Homeboy Music Studios,Taipei

OP:HIM Music Publishing Inc.

OP:Homeboy Music,Inc,Taiwan

SP:Sony Music Publishing(Pre)Ltd.Taiwan Branch

点击右上角即可分享
微信分享提示