突然意识到忘记扫描线怎么写叻,赶快补补。
我是离散化了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; }