题解:Luogu P3875 [TJOI2010] 被污染的河流

非常板的一道扫描线的题目,模板题可以参考 P5490【模板】扫描线 & 矩形面积并 这道题,这里就不再讲述扫描线的知识点了。

本题中,一条「河流」要么与 x 轴平行,要么与 y 轴平行,我们就可以分开考虑。此处题目非常坑,并未说明输入的端点的位置关系,所以要加上判断:

int s, d, f, g;
int x1, y1, x2, y2;
s = read();
d = read();
f = read();
g = read();
if(s == f) { // 平行于 y 轴
    x1 = s - 1, x2 = s + 1;
    y1 = min(d, g), y2 = max(d, g);
}
else { // 平行于 x 轴
    x1 = min(s, f), x2 = max(s, f);
    y1 = d - 1, y2 = d + 1;
}

然后就是正常的加矩形求面积并的问题了。本题由于坐标的范围较小,所以甚至可以不需要离散化。但是出于练习的考虑,下面的 AC 代码还是给出了离散化的操作。

完整代码:

#include <bits/stdc++.h>

#define N 400010
#define int long long
#define ls (p << 1)
#define rs (p << 1 | 1)
#define mid ((l + r) >> 1)
using namespace std;

int n;
int xx[N], xxx = 0;

struct linee {
    int l, r;
    int h;
    int tag;
}line[N];

struct treee {
    int cnt;
    int len;
}tree[N];

inline int read() {
    int x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-') f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = (x << 3) + (x << 1) + (ch ^ 48);
        ch = getchar();
    }
    return x * f;
}

inline void write(int x) {
    if (x < 0) {
        putchar('-');
        x = -x;
    }
    if (x > 9) write(x / 10);
    putchar(x % 10 + '0');
}

inline void writeln(int x) {
    write(x);
    putchar('\n');
}

inline bool cmp(linee x, linee y) {
    return x.h < y.h;
}

inline void build(int p, int l, int r) {
    tree[p].cnt = tree[p].len = 0;
    if(l == r) {
        return ;
    }

    build(ls, l, mid);
    build(rs, mid + 1, r);
}

inline void mdf(int p, int l, int r, int ql, int qr, int x) {
    if(ql <= l && r <= qr) {
        tree[p].cnt += x;
    }
    else {
        if(ql <= mid) {
            mdf(ls, l, mid, ql, qr, x);
        }
        if(qr > mid) {
            mdf(rs, mid + 1, r, ql, qr, x);
        }
    }

    if(tree[p].cnt) {
        tree[p].len = xx[r + 1] - xx[l];
    }
    else {
        if(l == r) {
            tree[p].len = 0;
        }
        else {
            tree[p].len = tree[ls].len + tree[rs].len;
        }
    }
}

signed main() {

    n = read();

    for(int i = 1; i <= n; ++i) {
        int s, d, f, g;
        int x1, y1, x2, y2;
        s = read();
        d = read();
        f = read();
        g = read();
        if(s == f) { // 平行于 y 轴
            x1 = s - 1, x2 = s + 1;
            y1 = min(d, g), y2 = max(d, g);
        }
        else { // 平行于 x 轴
            x1 = min(s, f), x2 = max(s, f);
            y1 = d - 1, y2 = d + 1;
        }
        xx[++xxx] = x1, xx[++xxx] = x2;
        line[i * 2 - 1] = {x1, x2, y1, 1};
        line[i * 2] = {x1, x2, y2, -1};
    }

    sort(xx + 1, xx + xxx + 1);

    xxx = unique(xx + 1, xx + xxx + 1) - xx - 1;
    sort(line + 1, line + 2 * n + 1, cmp);

    build(1, 1, xxx - 1);

    int ar = 0;

    for(int i = 1; i <= 2 * n - 1; ++i) {
        int ll = lower_bound(xx + 1, xx + xxx + 1, line[i].l) - xx;
        int rr = lower_bound(xx + 1, xx + xxx + 1, line[i].r) - xx;
        mdf(1, 1, xxx - 1, ll, rr - 1,  line[i].tag);
        ar += tree[1].len * (line[i + 1].h - line[i].h);
    }

    writeln(ar);
    return 0;
}

ps: 本题理论的最大面积为 \((10^5)^2 = 10^{10}\),超过了 int 的数据范围,所以要开 long long。虽然本题没有卡这个点,最好还是要注意一下。

posted @ 2025-10-14 23:39  amlhdsan  阅读(2)  评论(0)    收藏  举报