1 条题解

  • 0
    @ 2025-8-24 22:11:18

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar pengyirui
    最后在线时间:2025/8/15 17:00(预计)|互关条件:paste/c72gso4v#|宣团:(/team/100001)(/team/94966)

    搬运于2025-08-24 22:11:18,当前版本为作者最后更新于2025-08-11 17:06:03,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    前置芝士

    请务必学透再来做本题。
    线段树
    离散化

    扫描线算法介绍

    扫描线算法,专门用于解决多个矩形的面积并问题,算法实现流程跟名字一样。
    这是我从 OIwiki 搬运的算法实现过程视频。 通过观察视频,不难发现,计算共分成了 5 个部分,而每个部分的面积,就是一个矩形的面积,直接计算即可(不会的请回到小学再学一次)。
    矩形的高可轻易求得,在此不赘述。
    现在,重点就是求矩形的底了。再回顾一次视频中扫描方式,可以发现每次扫描到一根线段,我们就会把它加入或者删除。因为每次坐标均为整数,所以可以直接区间加上或者减去 1,最后区间求和即可。
    区间加,大家想到什么?线段树对吧。为了加速,考虑使用线段树维护。
    在线段树上进行修改,请自行学习,至于树上查询,因为我们需要全部的数量之和,直接查询根节点即可。
    整体时间复杂度:O(nlogn)O(n\log n),完全可以通过本题。 :::warning[小提醒] 别忘了离散化!!!
    别忘了离散化!!!
    别忘了离散化!!!
    :::

    正确性证明

    对于每行:因为每一次当前行中算的是覆盖的部分,重叠的部分只会计算一次,且不重不漏。(因为每次查找的是大于等于 1 的数字个数)。
    对于不同行:由于行不同,自然不会产生重叠。

    时间复杂度证明

    离散化:我采用 sort、unique 和 lower_bound 进行离散化,时间复杂度 O(nlogn)O(n\log n)
    区间修改操作:线段树操作,时间复杂度 O(nlogn)O(n\log n)
    查询操作:查询总和即可,时间复杂度 O(n)O(n)
    总和:省略常数项为 O(nlogn)O(n\log n)

    代码

    #include<bits/stdc++.h>
    #define int long long
    using namespace std;
    int n,m,a[3999999],tr[3999999],bj[3999999],lbj[3999999],tot,buc[1008617],len[1008617];
    struct nd
    {
        int l,r,y,k;
    }tree[1008617];
    void build(int l,int r,int now)
    {
    	len[now]=buc[r]-buc[l-1];
        if(l==r)return;
        int mid=l+((r-l)/2);
        build(l,mid,now*2);
        build(mid+1,r,now*2+1);
    }
    void update(int u) 
    { 
        if(lbj[u]>0)tr[u]=len[u];
        else tr[u]=tr[2*u]+tr[2*u+1];
    }
    void xg(int l,int r,int to,int l1,int r1,int now)
    {
    	if(l<=l1&&r1<=r)
    	{	
    		lbj[now]+=to;
    	    update(now);
    		return;
    	}
        int mid=l1+(r1-l1)/2;
        if(l<=mid)xg(l,r,to,l1,mid,now*2);
    	if(r>mid)xg(l,r,to,mid+1,r1,now*2+1);
        update(now);
    }
    bool cmp(nd aa,nd bb)
    {
        return aa.y<bb.y;
    }
    signed main()
    {
        cin>>n;
        for(int i=1;i<=n;i++)
        {
            int xl,xr,yl,yr;
            cin>>xl>>yl>>xr>>yr;
            tree[i].l=xl;
            tree[i].r=xr;
            tree[i].y=yl;
            tree[i].k=1ll;
            tree[i+n].l=xl;
            tree[i+n].r=xr;
            tree[i+n].y=yr;
            tree[i+n].k=-1ll;
            buc[++tot]=xl;
            buc[++tot]=xr;
        }
        sort(buc+1,buc+tot+1);
        tot=unique(buc+1,buc+1+tot)-buc-1;
         for(int i=1;i<=2*n;i++) 
    	{
            tree[i].l=lower_bound(buc+1,buc+1+tot,tree[i].l)-buc;
            tree[i].r=lower_bound(buc+1,buc+1+tot,tree[i].r)-buc;
        }
        sort(tree+1,tree+1+2*n,cmp);
        build(1,tot,1);
        int ans=0;
        for(int i=1;i<=2*n;i++) 
    	{
            ans+=(tree[i].y-tree[i-1].y)*tr[1];
            xg(tree[i].l+1,tree[i].r,tree[i].k,1,tot,1);
        }
        cout<<ans;
        return 0;
    }
    

    小练习

    别看我,我除了前两题全都不会做……
    【模板】离线二维数点
    矩形周长
    扇形面积并
    纵坐标扫描线
    洪水

    • 1

    信息

    ID
    4492
    时间
    1000ms
    内存
    512MiB
    难度
    5
    标签
    递交数
    0
    已通过
    0
    上传者