扫描线
这道题机房大佬早就会了,而我现在才刚刚懂那么一点点。。。qwq
个人感觉最主要的就是处理好每条线段之间的端点问题就好了
线段树的每个节点都对应了一条线段。考虑将线段树上节点对应的区间和横边建立映射关系。先看对于一个叶子节点x,建树时保证了tree[x].l=tree[x].r,但其保存的信息很显然不可能只是某条线段的一个端点(如果一条线段的两个端点重合,那么它实质上仅是一个点)。再看一个节点的左右儿子,同样地,建树的时候已经保证了左右儿子的区间不会重合(交集为空),但是看这样两条相邻线段:[1,2],[2,3]你会发现[1,2]∩[2,3]={2},也就是说左儿子的右端点和右儿子的左端点其实是重合的。所以如果想得太简单,就gg了。
考虑把线段树每个节点x对应的区间(tree[x].l,tree[x].r)不变,改变区间和横边的映射关系,具体为:节点x对应[ X[tree[x].l], X[tree[x].r+1] ]这条横边。可以看到,这里把右端点的对应关系给改了下,于是就兼容了。
Code:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 #define ll long long 3 #define ls x << 1 4 #define rs x << 1 | 1 5 #define y1 fnsdgnuyrghv//这里define是因为y1在<cmath>里有定义,然后机房大佬想出的骚操作,这样就不会冲突啦 6 #define y2 vsxdfjklvhfsdxoi 7 using namespace std; 8 const int N = 1e6 + 7; 9 int n; 10 ll x1, y1, x2, y2, X[N << 1]; 11 struct Scanline{ 12 ll l, r, h;//记录线段的左右端点和高度 13 int v;//标记判断是下面的线段还是上面的线段 14 bool operator < (const Scanline &b) { 15 return h < b.h;//按高度排序,保证每次都是先扫到下边 16 } 17 }line[N << 1]; 18 struct Tree{ 19 int l, r, sum;//l,r就是x这个点所包括的范围 20 ll len; 21 //sum是被覆盖过了几次,len是被覆盖长度 22 }t[N << 2]; 23 void build(int x, int l, int r) { 24 t[x].l = l, t[x].r = r; 25 t[x].len = t[x].sum = 0; 26 if (l == r) { 27 return; 28 } 29 int mid = (l + r) / 2; 30 build(ls, l, mid), build(rs, mid + 1, r); 31 } 32 void pushup(int x) { 33 int l = t[x].l, r = t[x].r; 34 if (t[x].sum) { 35 t[x].len = X[r + 1] - X[l]; 36 //如果被覆盖过就重新计算长度 37 } else { 38 t[x].len = t[ls].len + t[rs].len; 39 //否则就合并 40 } 41 } 42 void change(int x, ll L, ll R, int v) { 43 int l = t[x].l, r = t[x].r; 44 if (X[r + 1] <= L || R <= X[l]) { 45 return; 46 } 47 // 这里加等号的原因: 48 // 假设现在考虑 [2,5], [5,8] 两条线段,要修改 [1,5] 区间的sum 49 // 很明显,虽然5在这个区间内,[5,8] 却并不是我们希望修改的线段 50 // 所以总结一下,就加上了等号 51 if (L <= X[l] && X[r + 1] <= R) { 52 t[x].sum += v; 53 pushup(x); 54 return; 55 } 56 change(ls, L, R, v); 57 change(rs, L, R, v); 58 pushup(x); 59 } 60 int main () { 61 scanf("%d", &n); 62 for (int i = 1; i <= n; i++) { 63 scanf("%lld%lld%lld%lld", &x1, &y1, &x2, &y2); 64 X[2 * i - 1] = x1, X[2 * i] = x2; 65 line[2 * i - 1] = {x1, x2, y1, 1}; 66 line[2 * i] = {x1, x2, y2, -1}; 67 //存储每条线段的信息 68 } 69 n <<= 1; 70 sort(line + 1, line + 1 + n); 71 sort(X + 1, X + 1 + n);//按横坐标排序还要去重 72 int m = unique(X + 1, X + 1 + n) - X - 1; 73 build(1, 1, m - 1); 74 //m-1是因为右端点的对应关系已经修改了 75 ll ans = 0; 76 for (int i = 1; i < n; i++) { 77 change(1, line[i].l, line[i].r, line[i].v); 78 ans += t[1].len * (line[i + 1].h - line[i].h); 79 //注意是t[1].len 80 } 81 printf("%lld\n", ans); 82 return 0; 83 }