扫描线

Luogu

这道题机房大佬早就会了,而我现在才刚刚懂那么一点点。。。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:

 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 }
View Code

 

 

 

posted @ 2019-11-10 15:34  Sun-dial  阅读(142)  评论(0编辑  收藏  举报