首页 > 编程语言 >AcWing 算法提高课 线段树+扫描线法 求矩形之并的面积

AcWing 算法提高课 线段树+扫描线法 求矩形之并的面积

时间:2022-09-27 19:46:28浏览次数:59  
标签:cnt int 线段 tr seg 扫描线 ys y2 AcWing

例题:求解多个长方形之并的面积

https://www.acwing.com/problem/content/249/

蓝色表示长方形,红色表示扫描线

如下图所示,对于每一个横向的区间,在纵向维护线段树

根据纵向的累计长度,即可对每个横向区间求出面积

求面积的过程中,可以从左到右遍历区间(遍历除第一个点以外的分界点),遇到左边界,则将线段对应全部位置+1,右边界则-1。此时面积就是区间长度*全部大于0的位置的数量。

 

 

由于扫描线的性质(只查询节点1、加和减成对出现、cnt大于0则不需要向下递归等性质),不需要使用懒标记。只需维护当前节点的cnt和len,cnt表示当前区间整个被覆盖的次数,len表示cnt>0的区间总长度。

代码:(注意,例题中由于y存在小数,故需要离散化,并且线段树中的"点"代表一个区间)

 

#include<bits/stdc++.h>

#define fore(x,y,z) for(LL x=(y);x<=(z);x++)
#define forn(x,y,z) for(LL x=(y);x<(z);x++)
#define rofe(x,y,z) for(LL x=(y);x>=(z);x--)
#define rofn(x,y,z) for(LL x=(y);x>(z);x--)
#define pub push_back
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second

using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
typedef pair<LL,LL> PLL;
const int N=100010;
int T=1;
struct Segment
{
    double x,y1,y2;
    int k;
    bool operator<(const Segment &t) const
    {
        return x<t.x;
    }
}seg[N*2];//长方形的左右边,即需要用于添加和删除的线段
struct Node
{
    int l,r;
    int cnt;
    double len;
}tr[N*8];//最多2N个点,2*N-1个区间
int n;
vector<double> ys;//y离散化
int find(double y)//找到离散化后对应的下标
{
    return lower_bound(ys.begin(),ys.end(),y)-ys.begin();
}

void pushup(int u)
{
    if(tr[u].cnt) tr[u].len=ys[tr[u].r+1]-ys[tr[u].l];//区间右端点是y[r+1]
    else if(tr[u].l!=tr[u].r)
    {
        //由于modify中使用了pushup进行计算,
        //而没有对叶节点进行特殊,所以需要在这里判断
        tr[u].len=tr[u*2].len+tr[u*2+1].len;
    }
    else
        tr[u].len=0;
}
void build(int u,int l,int r)
{
    tr[u]={l,r,0,0};
    if(l!=r)
    {
        int mid=(l+r)/2;
        build(u*2,l,mid);
        build(u*2+1,mid+1,r);
    }
}

void modify(int u,int l,int r,int 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)/2;
        if(l<=mid) modify(u*2,l,r,k);
        if(r>=mid+1) modify(u*2+1,l,r,k);
        pushup(u);
    }
}
void YD()
{
    ys.clear();
    int cnt=0;
    fore(i,1,n)
    {
        double x1,x2,y1,y2;
        cin>>x1>>y1>>x2>>y2;
        seg[cnt++]={x1,y1,y2,1};
        seg[cnt++]={x2,y1,y2,-1};
        ys.pub(y1);ys.pub(y2);
    }
    sort(ys.begin(),ys.end());
    ys.erase(unique(ys.begin(),ys.end()),ys.end());
    sort(seg,seg+2*n);
    build(1,0,ys.size()-2);//每个y代表其后面的区间,故不需要建立最后一个y
    double res=0;
    fore(i,0,2*n-1)
    {
        if(i>0) res+=tr[1].len*(seg[i].x-seg[i-1].x);
        modify(1,find(seg[i].y1),find(seg[i].y2)-1,seg[i].k);
        //对于n个点共有n-1个区间,可以用每一个点代表以其为左端点的区间,
        //故find(seg[i].y2-1),表示不修改y2后面的区间。
    }
    cout<<"Test case #"<<T<<endl;
    cout<<"Total explored area: "<<fixed<<setprecision(2)<<res<<endl; 
    cout<<endl;
}
 
int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);

    
    while (cin>>n,n)
    {
        YD();
        T++;
    }
    return 0;
}
View Code

 

标签:cnt,int,线段,tr,seg,扫描线,ys,y2,AcWing
From: https://www.cnblogs.com/ydUESTC/p/16735703.html

相关文章

  • 【luogu CF1109E】Sasha and a Very Easy Test(线段树)
    SashaandaVeryEasyTest题目链接:luoguCF1109E题目大意维护一个长度为n的序列,有区间乘,单点除(保证能整除),区间求和答案对p取模。p不一定是质数。思路麻了考场......
  • 两个和最大的区间(线段树+单调栈+dp)
      胜哥投喂的一道面试题  题意:有一个环形数组\(a\),找出两个不重叠的子串,是的这两个区间内所有的数加起来的和最大。  数据范围:\(1\leqn\leq10^5,\left|......
  • 线段树学习笔记(基础&进阶)(一) | P3372 【模板】线段树 1 题解
    什么是线段树线段树是一棵二叉树,每个结点存储需维护的信息,一般用于处理区间最值、区间和等问题。线段树的用处对编号连续的一些点进行修改或者统计操作,修改和统计的复杂......
  • acwing148. 合并果子
    acwing148.合并果子原题链接:https://www.acwing.com/problem/content/150/思路贪心使用集合角度思考,需要去证明最优解在某个集合的子集之中,其他的子集就可以摒弃掉。......
  • mex(权值线段树+线段树上二分)
      思路:首先看到区间维护,想到线段树,但是很明显无法用线段树直接维护区间最小没出现过的自然数,因为假若一个节点的左右儿子节点的值都为0,我们是无法推断出父节点的值是几......
  • acwing913. 排队打水
    acwing913.排队打水原题链接:https://www.acwing.com/problem/content/description/915/思路贪心的题:1.猜想2.证明自己的猜想猜想:所有数从小到大排序,总的等待时间最小......
  • 树状数组和线段树资料
    例题和代码模板:https://www.cnblogs.com/pavtlly/p/9979702.html看老师这个博客链接吧 https://zhuanlan.zhihu.com/p/106118909线段树资料 线段树进阶资料(有兴......
  • acwing 4619. 减法操作
    acwing4619.减法操作原题链接:https://www.acwing.com/problem/content/4622/思路这个题两种操作顺序是先进行哪个操作都是可以的。第一个操作将某个数减2,只要是偶数就......
  • acwing 4618. 两个素数
    两个素数原题链接:https://www.acwing.com/problem/content/4621/思路本来我以为是要判断是不是素数但是y总后来讲的时候,我才发现题目保证一定有解,也就是说x一定会由两......
  • 关于线段树的一点小笔记
    关于线段树的一点小笔记线段树:线段树是一棵天生支持单点修改、查询和区间查询的一棵完全二叉树,其单点修改、查询和区间修改的时间复杂度均为\(\Theta(\lgn)\)在区......