题解 P5490 【【模板】扫描线】
CYJian
2019-09-21 23:16:02
为啥要离散化啊。。多麻烦。。。
这里提供一种偷懒的做法:
动态开点线段树+标记永久化, 就可省略掉离散化啦!
大概的思路和其他的题解差不多, 这里就不赘述了, 下面提供做法的实现, 主要在注释中说明。
```cpp
#include <bits/stdc++.h>
using namespace std;
#define ge getchar()
#define Re read()
inline int read() {
int x = 0, ch;
while(!isdigit(ch = ge)) ;
while(isdigit(ch)) x = x * 10 + (ch ^ 48), ch = ge;
return x;
}
const int MAXN = 200100;
struct Node {
int l, r, p;
Node() {}
Node(int l, int r, int p):l(l), r(r), p(p) {}
friend bool operator < (Node a, Node b) { return a.p < b.p; }
}Ins[MAXN + 1], Del[MAXN + 1];
struct Segment_Tree { // 下面是线段树
#define ls T[x].r
#define rs T[x].l
struct Node { int l, r, sum, tg; } T[MAXN << 6]; // 每一次修改最多修改的点数有log级别, 所以动态开点要乘log(最好再乘2)
int ct;
inline void pushup(int x, int l, int r) {
if(T[x].tg) T[x].sum = r - l + 1; // 如果这个点上有标记, 那么就说明被线段覆盖了, 长度就是 r-l+1
else T[x].sum = T[ls].sum + T[rs].sum; // 没有标记就从线段树上的两个儿子节点继承
}
// 没有pushdown 标记永久化节省空间
inline void Modify(int&x, int l, int r, int L, int R, int v) {
if(!x) x = ++ct; // 动态开点
if(L <= l && r <= R) {
T[x].tg += v; // 更新标记
pushup(x, l, r); // 更新节点值
return ;
} int mid = (l + r) >> 1;
if(L <= mid) Modify(ls, l, mid, L, R, v);
if(mid < R) Modify(rs, mid + 1, r, L, R, v);
pushup(x, l, r); // 别忘了更新
}
inline int Query() { return T[1].sum; } // 询问沿y轴截线段的长度和, 相当于是线段树的1号节点存的长度和。
#undef ls
#undef rs
}seg; // 上面是线段树
int main() {
int n = Re, mi = 1e9, mx = 0;
for(int i = 1; i <= n; i++) {
int lx = Re, ly = Re, rx = Re, ry = Re;
Ins[i] = Node(ly, ry, lx);
Del[i] = Node(ly, ry, rx);
mi = min(mi, ly);
mx = max(mx, ry);
} int rt = 0; long long res = 0;
sort(Ins + 1, Ins + 1 + n); Ins[n + 1] = Node(0, 0, 2e9);
sort(Del + 1, Del + 1 + n); Del[n + 1] = Node(0, 0, 2e9);
for(int i = 1, a = 1, b = 1, N = n << 1, las = 0; i <= N; i++) {
int x = min(Ins[a].p, Del[b].p);
res += 1LL * (x - las) * seg.Query(), las = x; // 求出上次询问的横坐标差 * 当前扫描线上的沿y轴的截线段长度和。
if(Ins[a].p == x) seg.Modify(rt, mi, mx, Ins[a].l, Ins[a].r - 1, 1), ++a; // 如果当前这个x坐标需要插入一条线段, 那么就+1
else seg.Modify(rt, mi, mx, Del[b].l, Del[b].r - 1, -1), ++b; // 如果删除就-1
//这一段为啥要将r-1呢? 因为线段树里头的线段默认计算的长度为 r-l+1, 这个时候我们把r-1之后才符合计算面积的公式, 而且所有插入的线段的r都减了1之后计算并集并不会影响。
} printf("%lld\n", res);
return 0; // 完结撒花
} // 没有离散化真清爽啊(雾)
```