比亚特兰蒂斯的数据要简化,同样是扫描线的入门题目,不需要对数据进行离散化,题目这次要求的是周长,和面积比起来,我们需要在线段树中增加更多成员。记录下当前线段树有效长度len和记录线段树当前线段数量的num,同时设置rb,lb标记左右节点是否被覆盖。每一次ans增加的值为当前有效长度+(上次线段数量-本次线段数量)*2。最重要的还是pushup函数,参考别人的博客看的实在有点费解,大概还需要点时间来消化吧。

#include<bits/stdc++.h>
#define ll long long
#define ls(i) i<<1
#define rs(i) i<<1|1
using namespace std;
const int N = 2e5+10;
struct node{
    int l,r;        // 左右下标
    int len;        // 区间长度
    int cover;        // 被覆盖多少次
    int num;        // 区间上有多少条线段
    int lf,rf;        // 左右区间值域
    bool lb,rb;        // 左右孩子是否被覆盖
}sgt[N<<3];
struct L{
    int x,y1,y2;    // 垂直x轴的线段
    int state;        // 入边/出边
    bool operator<(L oth)const{
        if(x==oth.x)    return state > oth.state;
        return x < oth.x;
    }
}line[N];
int y[N];
void pushup(int rt){
    if(sgt[rt].cover){    // 被完全覆盖
        sgt[rt].len = sgt[rt].rf - sgt[rt].lf;
        sgt[rt].num = 1;
        sgt[rt].lb = sgt[rt].rb = 1;
    }
    else if(sgt[rt].l+1==sgt[rt].r){    // 节点为叶子,且没被覆盖
        sgt[rt].rb = sgt[rt].lb = 0;
        sgt[rt].len = sgt[rt].num = 0;
    }else{                            // 用左右儿子更新自己
        sgt[rt].rb = sgt[rs(rt)].rb;
        sgt[rt].lb = sgt[ls(rt)].lb;
        sgt[rt].len = sgt[ls(rt)].len + sgt[rs(rt)].len;
        sgt[rt].num = sgt[ls(rt)].num + sgt[rs(rt)].num ;
        if(sgt[ls(rt)].rb && sgt[rs(rt)].lb) sgt[rt].num--;        // 左儿子右边被覆盖,右儿子左边被覆盖,是被同一条线段覆盖,个数--
    }
}
// 建树
void build(int l,int r,int rt=1){
    sgt[rt].l = l;    sgt[rt].r = r;    sgt[rt].num = 0;
    sgt[rt].lf = y[l];    sgt[rt].rf = y[r];
    if(l+1>=r)    return ;
    int mid = (l+r)>>1;
    build(l,mid,ls(rt));
    build(mid,r,rs(rt));
}
// 加边
void modify(int yl,int yr,int op,int rt=1){
    int lf = sgt[rt].lf, rf = sgt[rt].rf;
    if(yl<=lf && yr>=rf){    // 被覆盖
        sgt[rt].cover += op;
        pushup(rt);
        return ;
    }
    if(yl<sgt[ls(rt)].rf)    modify(yl,yr,op,ls(rt));    // 落入左儿子
    if(yr>sgt[rs(rt)].lf)    modify(yl,yr,op,rs(rt));    // 落入右儿子
    pushup(rt);
}

int main(){
    int n,x1,x2,y1,y2;
    scanf("%d",&n);
    for(int i=1;i<=n;++i){
        scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
        y[i] = y1;    y[i+n] = y2;
        line[i]=(L){x1,y1,y2,1};
        line[i+n]=(L){x2,y1,y2,-1};
    }
    sort(y+1,y+1+n*2);
    sort(line+1,line+1+n*2);
    int m = unique(y+1,y+1+n*2)-y-1;// 离散化 获得y值个数
    build(1,m,1);    // 根据y的个数建树
    int ans = 0,last = 0,lines = 0;
    for(int i=1;i<=n*2;++i){    // 扫描
        modify(line[i].y1,line[i].y2,line[i].state,1);    // 加入当前边
        ans += lines *2LL* (line[i].x - line[i-1].x);    // 算平行于x轴的周长,每条线段贡献两次
        ans += abs(sgt[1].len - last);    // 计算垂直x轴的长度,根节点存的是当前扫描线的长度,与上一次做差,即为变化的长度(变长为入边,变少为出边) 
        last = sgt[1].len;    // 上一次的长度
        lines = sgt[1].num;    //  上一次线段个数
    }
    printf("%d\n",ans);
}