测试一下随笔功能

大标题

二级标题

一级标题

二级标题

  1. a
  2. b
  3. c

红色

q4378rqf34g

wef

2

3

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 10010;

int n;
//线段结构体,包含竖线的x,y1,y2三个位置信息
//k表示这条线段是一个矩形的开始还是结束
//结构体数组用来存储所有线段,因为还要先对所有线段排序,按x从小到大的顺序来扫描
struct Segment
{
    int x, y1, y2;
    int k;
    bool operator< (const Segment &t)const
    {
        return x < t.x;
    }
}seg[N * 2];

//线段树结点存储的是y轴方向区间的被覆盖长度和被覆盖次数
//l,r分别表示当前区间的端点
//cnt表示结点区间被覆盖次数,len表示结点区间内被覆盖的长度
//len是真正关心的,cnt相当于一个标记用来正确求出len
struct Node
{
    int l, r;
    int cnt, len;
}tr[N * 4];

//递归
void pushup(int u)
{
    //结点区间被覆盖过一次以上,那len就等于区间长度
    //+1是因为线段树的叶节点是单位线段,而不是点,比如区间包含单位线段1和单位线段2,len=2-1+1=2
    if (tr[u].cnt > 0) tr[u].len = tr[u].r - tr[u].l + 1;
    //否则递归下去,终止条件:当l==r表示是一条单位线段,且没被覆盖(else),所以len=0
    else if (tr[u].l == tr[u].r) tr[u].len = 0;
    //不是叶节点len=两个子节点的len之和
    else tr[u].len = tr[u << 1].len + tr[u << 1 | 1].len;
}

//递归
void build(int u, int l, int r)
{
    //初始化,因为是边扫描边维护线段树,所以一开始相当于空白区域,全部区间都没有覆盖
    //cnt和len也应该置零的,但是全局数组默认置零了
    tr[u] = {l, r};
    if (l == r) return;
    //递归下去,完成初始化操作
    int mid = l + r >> 1;
    build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
}

//修改结点中l到r区间的cnt
void modify(int u, int l, int r, int k)
{
    //区间完全包含结点,直接修改结点的cnt,修改完cnt会影响到当前节点区间的覆盖性
    //即len需要重新计算,调用pushup
    if (tr[u].l >= l && tr[u].r <= r)
    {
        tr[u].cnt += k;
        pushup(u);
    }
    //区间不完全覆盖结点,递归下去修改子节点,然后pushup更新
    else
    {
        int mid = tr[u].l + tr[u].r >> 1;
        if (l <= mid) modify(u << 1, l, r, k);
        if (r > mid) modify(u << 1 | 1, l, r, k);
        pushup(u);
    }
}




int main()
{
    scanf("%d", &n);
    int m = 0;
    for (int i = 0; i < n; i ++ )
    {
        int x1, y1, x2, y2;
        scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
        seg[m ++ ] = {x1, y1, y2, 1};
        seg[m ++ ] = {x2, y1, y2, -1};
    }

    sort(seg, seg + m);

    build(1, 0, 10000);

    int res = 0;
    
    //扫描线法,从左到右扫描竖线,每次扫描到一条竖线,都会修改当前线段树的状态
    for (int i = 0; i < m; i ++ )
    {
        if (i > 0) res += tr[1].len * (seg[i].x - seg[i - 1].x);
        modify(1, seg[i].y1, seg[i].y2 - 1, seg[i].k);
    }
    
    printf("%d\n", res);

    return 0;
}




posted @ 2022-11-17 17:48  林动  阅读(19)  评论(0编辑  收藏  举报