突然意识到忘记扫描线怎么写叻,赶快补补。
我是离散化了y坐标,将x坐标排序,线段树里面下标是离散后的y,存的是实际的覆盖了的y的长度。
主要是update的时候,如果当前整个区间都被覆盖叻,那么直接更新成这段的实际长度。如果已经到叶子节点,长度就是0,剩下是没有被覆盖完的情况,就用左右儿子更新。
查询时只需要O(1)取出当前根节点即可,因为存的是当前全局中被覆盖了的y区间,乘上x区间(因为是坐标轴所以左闭右开)即可。
然后离散化和update的时候都要左闭右开!!最好记一下套路吧...
#include<bits/stdc++.h>
using namespace std;
int n;
int X1[105], Y1[105], X2[105], Y2[105];
struct TR {
int X, Y1, Y2, opt;
TR(int X = 0, int Y1 = 0, int Y2 = 0, int opt = 0) :
X(X), Y1(Y1), Y2(Y2), opt(opt) { }
} line[505];
bool cmp(TR a, TR b) { return a.X < b.X; }
long long tag[505];
long long tree[505], a[1005];
void push_down(int nd, int l, int r) {
if(tag[nd]) tree[nd] = a[r + 1] - a[l];//////////////左闭右开
else if(l == r) tree[nd] = 0;
else tree[nd] = tree[nd << 1] + tree[nd << 1 | 1];
}
void add(int nd, int l, int r, int L, int R, int opt) {
if(l >= L && r <= R) {
tag[nd] += opt;
push_down(nd, l, r);
return ;
}
push_down(nd, l, r);
int mid = (l + r) >> 1;
if(L <= mid) add(nd << 1, l, mid, L, R, opt);
if(R > mid) add(nd << 1 | 1, mid + 1, r, L, R, opt);
push_down(nd, l, r);
}
int main() {
freopen("olddriver.in", "r", stdin);
freopen("olddriver.out", "w", stdout);
scanf("%d", &n);
int tot = 0;
for(int i = 1; i <= n; i ++) {
scanf("%d%d%d%d", &X1[i], &Y1[i], &X2[i], &Y2[i]);
a[++tot] = Y1[i];
line[tot] = TR(X1[i], Y1[i], Y2[i], 1);
a[++tot] = Y2[i];
line[tot] = TR(X2[i], Y1[i], Y2[i], -1);
}
sort(a + 1, a + 1 + tot);
sort(line + 1, line + 1 + tot, cmp);
int m = unique(a + 1, a + 1 + tot) - a - 1;
long long ans = 0;
for(int i = 1; i < tot; i ++) {
int L = lower_bound(a + 1, a + 1 + m, line[i].Y1) - a;
int R = lower_bound(a + 1, a + 1 + m, line[i].Y2) - a - 1;///////////左闭右开
add(1, 1, m, L, R, line[i].opt);
ans += 1ll * tree[1] * (line[i+1].X - line[i].X);
}
printf("%lld", ans);
return 0;
}