其他分享
首页 > 其他分享> > 线段树扫描求面积

线段树扫描求面积

作者:互联网

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

using namespace std;

const int N = 10010;

int n;
struct Segment
{
    int x, y1, y2;
    int k;
    bool operator< (const Segment &t)const       //按横坐标排序
    {
        return x < t.x;
    }
}seg[N * 2];        //记录线段,包括横坐标,和两个纵坐标,k表示该线段是要进来的,还是要出去的
struct Node         //线段树节点
{
    int l, r;       //记录左右边界
    int cnt, len;   //len记录本节点的覆盖长度(纵坐标方向)
}tr[N * 4];         //树一共有lg(N)+2层,也就是有4*N-1个节点

void pushup(int u)      //利用本身和子节点的信息去更新自己
{
    if (tr[u].cnt > 0) tr[u].len = tr[u].r - tr[u].l + 1;     //如果前面加上k使得cnt大于零了,本段被覆盖了,正好修改
    else if (tr[u].l == tr[u].r) tr[u].len = 0, tr[u].cnt = 0; //如果是叶子节点,len为0,cnt为0
    else tr[u].len = tr[u << 1].len + tr[u << 1 | 1].len;  //那现在就是cnt小于零而且不是叶子节点,也就是本段未被完全覆盖
    //但是可能部分覆盖,那就是两个子节点覆盖范围之和
}

void build(int u, int l, int r)    //建树,无需多言
{ 
    // cout<<u<<endl;
    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);
}

void modify(int u, int l, int r, int k)      //对树进行修整,u是修改的节点序号,l,r是范围,k表示是加入还是退出
{
    if (tr[u].l >= l && tr[u].r <= r)        //该节点已经完全被包括在修改的范围中
    {
        tr[u].cnt += k;                      //此段覆盖次数改变
        pushup(u);                           //对上修改
    }
    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);                           //对上修改,这里pushup放在最后一行是关键,也就是pushup只根据子节点和自己的信息来更新自己
    }
}

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);            //以后每一次等于加入本段时
    }                                                   //y轴投影的覆盖长度乘以和上次的线段的距离
                                                        //然后加入本线段,对树进行修整
                                                        //这里写成y2-1是因为线段树看记录的最终是一个点,这里认为点的长度是1
    printf("%d\n", res);

    return 0;
}

标签:cnt,int,扫描,线段,面积,tr,len,seg
来源: https://www.cnblogs.com/sherkevin/p/16273765.html