• 扫描线


    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
    ", ans);
    82     return 0;
    83 }
    View Code
  • 相关阅读:
    117. 填充每个节点的下一个右侧节点指针 II
    116. 填充每个节点的下一个右侧节点指针
    114. 二叉树展开为链表
    9.5 NLP slide: 第二课 语言模型
    165. 比较版本号
    143. 重排链表
    147. 对链表进行插入
    127. 单词接龙
    129. 求根到叶子节点数字之和
    95. 不同的二叉搜索树 II 递归
  • 原文地址:https://www.cnblogs.com/Sundial/p/11830445.html
Copyright © 2020-2023  润新知