zoukankan      html  css  js  c++  java
  • Luogu P5490 【模板】扫描线

    传送门

    作为给dtx的妹子讲题的交换他给我讲的

    果然线段树最可爱了w


    扫描线可以用来求矩形的面积并…

    一个平面上有一些有重叠的矩形,求他们的并集的面积。

    直接放网上的图了x

    对于每个矩形,将y坐标拆成两个修改操作(插入和删除),从下到上排序;

    将x坐标unique离散化,从左到右排序,用线段树维护切割出来的每条线段。

    假设有一根从x轴出发,向上移动的线——也就是扫描线!

    遇到修改时,就在线段树上对应区间加上或减掉线段,答案加上(当前线段长度之和)*(这个修改到下个修改的y坐标之差)。

    线段树中维护什么信息?

    sum[i]表示区间i的线段总长度。显然,sum[i] = sum[ls]+sum[rs]。

    但是对于插入和删除,不能直接修改sum的值,因为一个线段可能被多个矩形覆盖...删除了一个之后还可能有另一个。

    lazy[i]表示区间i被几个矩形覆盖。这样,修改的时候直接在lazy上++--就好了。

    而且一定是先加再减,所以lazy标记一定不会为负数。

    可以发现,每次的查询操作都是查询整个区间。那么只需要考虑上传,不用下传。

    若lazy[i]>0,即被覆盖,则sum[i] = 区间i的长度;

    否则,sum[i] = sum[ls]+sum[rs]。

    注意:

    最后一个修改要忽略!因为没有n+1了。

    在传统的——维护点的线段树里,(1,4) = (1,2)+(3,4);

    但维护线段时,线段(2,3)被忽略掉了。

    所以更改一下定义:区间[L,R]对应线段(l,r+1)。这样保证了线段都是长为R-L+1的线段。

    修改时,因为没法确定哪里有线段,所以从整个区间开始,两个儿子都要递归。

    当完全偏离(L >= r[now]+1 || R <= l[now])时,就可以返回了;

    当完全覆盖(L <= l[now] && r[now]+1 <= R)时,就打上lazy。

    操作完一个节点并上传,判断lazy、更新sum的值时,

    sum[now] = sum[ls] + sum[rs]可能出现越界的情况,因为叶子节点并没有儿子!

    所以要么特判一下,要么再开2倍空间…

    (哎...想起了第一次写线段树1的时候的事了...)

     .

    代码如下

    #include<cstdio>
    #include<iostream>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    #define MogeKo qwq
    using namespace std;
    #define ls (now<<1)
    #define rs (now<<1|1)
    
    const int maxn = 2e5+10;
    int n,x_1,y_1,x_2,y_2,tot;
    long long ans;
    int X[maxn<<1];
    int l[maxn<<2],r[maxn<<2],sum[maxn<<2],lazy[maxn<<2];
    
    struct ScanLine {
        int l,r,h,val;
        bool operator < (const ScanLine & N) const {
            if(h == N.h) return val > N.val;
            return h < N.h;
        }
    } line[maxn<<1];
    
    void build(int L,int R,int now) {
        l[now] = L;
        r[now] = R;
        sum[now] = lazy[now] = 0;
        if(L == R) return;
        int mid = (l[now]+r[now]) >> 1;
        build(L,mid,ls);
        build(mid+1,R,rs);
    }
    
    void pushup(int now) {
        if(lazy[now])
            sum[now] = X[r[now]+1] - X[l[now]];
        else if(l[now] == r[now])
            sum[now] = 0;
        else
            sum[now] = sum[ls] + sum[rs];
    }
    
    void modify(int L,int R,int c,int now) {
        if(L >= X[r[now]+1] || R <= X[l[now]])
            return;
        if(L <= X[l[now]] && X[r[now]+1] <= R) {
            lazy[now] += c;
            pushup(now);
            return;
        }
        modify(L,R,c,ls);
        modify(L,R,c,rs);
        pushup(now);
    }
    
    int main() {
        scanf("%d",&n);
        for(int i = 1; i <= n; i++) {
            scanf("%d%d%d%d",&x_1,&y_1,&x_2,&y_2);
            line[2*i-1] = (ScanLine) {x_1,x_2,y_1,1};
            line[2*i] = (ScanLine) {x_1,x_2,y_2,-1};
            X[2*i-1] = x_1;
            X[2*i] = x_2;
        }
        n <<= 1;
        sort(line+1,line+n+1);
        sort(X+1,X+n+1);
        tot = unique(X+1,X+n+1)-X-1;
        build(1,tot-1,1);
        for(int i = 1; i < n; i++) {
            modify(line[i].l,line[i].r,line[i].val,1);
            ans += (long long)sum[1] * (line[i+1].h - line[i].h);
        }
        printf("%lld",ans);
        return 0;
    }
  • 相关阅读:
    使用padding值控制控件的隐藏与显示
    首篇 sdk 之 AlertDialog
    eclipse中svn项目重定向地址
    Activity回传值报错:Failure delivering result ResultInfo{who=null,request=7,result = 0,data=null}
    常见字符集&乱码问题
    rhel 6.x 使用 udev scsi rules 配置裸设备
    rsync 同步文件
    debian 8.2 + apt-get + mongodb 3.2 + replica set
    debian 8.2 + apt-get + mongodb 3.2
    oracle virtualbox 扩大虚拟机硬盘
  • 原文地址:https://www.cnblogs.com/mogeko/p/11801282.html
Copyright © 2011-2022 走看看