HDU1255 覆盖的面积(线段树+扫描线+离散化)

HDU1255 覆盖的面积(线段树+扫描线+离散化)

模板题

给定平面上若干矩形,求出被这些矩形覆盖过至少两次的区域的面积.

在这里插入图片描述

Input
输入数据的第一行是一个正整数T(1<=T<=100),代表测试数据的数量.每个测试数据的第一行是一个正整数N(1<=N<=1000),代表矩形的数量,然后是N行数据,每一行包含四个浮点数,代表平面上的一个矩形的左上角坐标和右下角坐标,矩形的上下边和X轴平行,左右边和Y轴平行.坐标的范围从0到100000.

注意:本题的输入数据较多,推荐使用scanf读入数据.
Output
对于每组测试数据,请计算出被这些矩形覆盖过至少两次的区域的面积.结果保留两位小数.
Sample Input
2 5
1 1 4 2
1 3 3 7
2 1.5 5 4.5
3.5 1.25 7.5 4
6 3 10 7
3
0 0 1 1
1 0 2 1
2 0 3 1
Sample Output
7.63
0.00

题解
让你求解所有矩形覆盖的面积和,或者是周长和,如果用寻常的方法,非常之麻烦,而且效率也不高,这里就会用到线段树的扫描线

扫描线应对方案:
由于题目提供的矩形比较多,坐标也很大,所以坐标需要离散化,可以按照题目要求或者自己的喜好,离散横坐标或者纵坐标都可以,这里讲的都是离散横坐标,不离散纵坐标。

假设有一条扫描线,从下往上扫描过整个多边形叠加的区域。如果是竖直方向上扫描,则是离散化横坐标,如果是水平方向右扫描,在进行扫描前,要保存好所有矩形的上边和下边,并且按照它们所处的高度进行排序,另外我们给上边赋值为-1,给下边赋值为1,接着我们用一个结构体来保存所有矩形的上边和下边,其中还有两条边不用管,因为他们已经包括在了高度中,高度差便能得到边的长度。

然后将扫描线从下往上扫描,每遇到一条上边或者下边就停下来,将这条线段加入到总区间上,下边赋值是1,扫描到下边的话相当于往总区间插入一条线段,上边为-1,扫描到上边相当于在总区间删除一条线段(也可以理解为当插入1的时候我们的扫描线在一个矩形中,当插入-1的时候证明我们的扫描线离开了一个矩形的区域,如此可以知道,区间不会出现负数的情况,因为永远都是下边1 >= 上边-1,他们相加是永远大于等于零的)

然后用下一条边的高度减去当前这条边的高度,乘上总区间被覆盖的长度,就能得到最终的面积

下面是pushup具体含义 主要依靠Ctrl+v
如果当前的位置为叶子节点,Cnt[o] == 1话证明被完全覆盖了,进行赋值。
如果为Cnt[o] == 0且还是叶子节点,这个节点一定为0。
如果当前位置不是叶子节点那么我们就要小心一点了,如果Cnt[o] == 0话,我们能说他没有被覆盖吗,不能,只能说没有被完全覆盖,为什么呢,因为他的子节点可能被完全覆盖了,但是父亲节点没有覆盖,那么怎么完全的不漏的得到这个区间被完全覆盖的区间大小呢,可以直接通过子节点覆盖的值
Sum[o] = Sum[o << 1] + Sum[o << 1|1]得到rt这个位置总覆盖区间

上面是覆盖一次的做法,那覆盖两次的做法是什么呢?

那么我们就要多增加一个数组用来记录覆盖了两次或者两次以上的区间大小。
Sum[o]表示覆盖了一次的区间
Sum2[o]表示覆盖了两次的区间
最开始跟上面的没有什么区别
如果Cnt[o] >= 2的话,很明显这个区间一定都被覆盖了至少是两次
如果Cnt[o] == 1的话,这里大家可以思考一下,直接从Sum2[o] = Sum[o << 1] + Sum[o << 1|1]
这里为什么是从子节点传上来呢,如果Cnt[o] == 1的话,证明此时这个位置已经被覆盖了一次,如果子节点也被覆盖了一次那不就是子节点被覆盖了两次吗,如此直接传给父亲节点,表示这个区间被覆盖了两次的答案
如果Cnt[o] == 0的话且还是叶子节点,sum2就是0。
如果不是,证明这个地方没有被覆盖一次,那么我们只有从子节点将覆盖了两次的结果传来了,Sum2[o] = Sum2[o << 1] + Sum2[o << 1|1];

线段是维护的是区间没有l==r 注意注意在注意 还要注意左右儿子的范围
当根节点为1-4时左儿子为1-2,右儿子为2-4.不要漏掉区间

上代码

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<stack>
#include <cstdlib>
#include <functional>
#define mem(a,x) memset(a,x,sizeof(a))
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
typedef long long ll;
typedef unsigned long long ull; // %llu
const double PI = acos(-1.0);
const double eps = 1e-6;
const int mod=1e9+7;
const int INF = -1u>>1;
const int maxn = 1e4+5;
struct edge         //存每一条平行x轴的边
{
    double x1,x2,h; //分别表达此条边的左右端点和y坐标
    int cnt;        //入边为1,出边为-1,方便更新。。重点
    edge () {}
    edge(double x1,double x2,double h,int cnt):x1(x1),x2(x2),h(h),cnt(cnt) {};
    friend bool operator < (edge a,edge b)
    {
        return a.h<b.h;
    }
} p[maxn];
struct node         //tree
{
    int l,r,o,cnt;
    double sum,sum2;        //sum为覆盖大于一次的长度,sum2为覆盖大于两次的长度
} t[4*maxn];
double X[maxn];
void build(int l,int r,int o)
{
    t[o].l=l;
    t[o].r=r;
    t[o].sum=0;
    t[o].sum2=0;
    t[o].cnt=0;
    if(l==r-1)              //注意叶子节点的范围,
        return ;
    int mid=(l+r)>>1;
    build(l,mid,o<<1);      //注意左右儿子的范围 当根节点为1-4时左儿子为1-2,右儿子为2-4.不要漏掉区间。
    build(mid,r,o<<1|1);    //维护的是l-r这个区间的cnt,sum,sum2,不存在l==r这种情况
}
void pushup(int o)      //关键
{
    //维护覆盖一次的面积
    if(t[o].cnt>=1)
        t[o].sum=X[t[o].r]-X[t[o].l];       //如果当前节点被覆盖大于一次,记录到sum中 此值即为此区间范围
    else if(t[o].l==t[o].r-1)               //如果当前节点没有被覆盖并且为叶子节点 值就是0 很好理解8
        t[o].sum=0;
    else                                    //如果当前节点没有被覆盖 注意这里覆盖的意思是指没有完全被覆盖,所以当前值需要左右儿子相加求得
        t[o].sum=t[o<<1].sum+t[o<<1|1].sum;

    //维护覆盖两次的面积
    if(t[o].cnt>=2)                 //如果当前节点被覆盖大于两次,直接记录sum3中, 此值即为此区间范围
        t[o].sum2=X[t[o].r]-X[t[o].l];
    else if(t[o].l==t[o].r-1)           //如果当前节点为叶子节点 值就是0 因为我sum2维护的就是大于2次覆盖的长度
        t[o].sum2=0;
    else if(t[o].cnt==1)                //如果当前节点被覆盖了一次 表示当前节点肯定被完全覆盖了一次 但不知哪些区域覆盖了两次 所以只需加上左右儿子覆盖1次的长度即为当前节点覆盖2次以上的长度
        t[o].sum2=t[o<<1].sum+t[o<<1|1].sum;
    else                                //此处表示节点没有被完全覆盖,所以结果只能由左右儿子覆盖2次的长度相加得出来
        t[o].sum2=t[o<<1].sum2+t[o<<1|1].sum2;
}
void update(int o,int l,int r,edge x)
{
    if(t[o].l==l&&t[o].r==r)    //如果当前范围属于当前节点
    {
        t[o].cnt+=x.cnt;    //加入cnt 表示次节点被覆盖的次数 方便pushup
        pushup(o);
        return ;
    }
    int mid=(t[o].l+t[o].r)>>1;
    if(r<=mid)
        update(o<<1,l,r,x);
    else if(l>=mid)
        update(o<<1|1,l,r,x);
    else
    {
        update(o<<1,l,mid,x);
        update(o<<1|1,mid,r,x);
    }
    pushup(o);      //不要忘记
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n;
        scanf("%d",&n);
        mem(t,0);
        double ans=0;
        for(int i=1; i<=n; i++)
        {
            double x1,x2,y1,y2;
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            p[2*i-1]=edge(x1,x2,y1,1);           //存入每条平行x轴的边
            p[2*i]=edge(x1,x2,y2,-1);
            X[2*i-1]=x1;                //存入每条边的左右端点
            X[2*i]=x2;
        }
        sort(X+1,X+2*n+1);          //按照x坐标从小到大排序
        sort(p+1,p+2*n+1);          //按照y坐标从小到大排序边
        int m=unique(X+1,X+2*n+1)-X-1;    //去重x坐标相同元素,方便离散化
        build(1,m,1);           //建树 这里主根的l=1,r=m,表示的是1-r这个区间所维护的值。所以叶子节点只可能是l=x,r=x+1,不存在l==r。
        for(int i=1; i<=2*n-1; i++)
        {
            int l=lower_bound(X+1,X+m+1,p[i].x1)-X;     //从下到上遍历每一条边,二分确定左右区间
            int r=lower_bound(X+1,X+m+1,p[i].x2)-X;
            update(1,l,r,p[i]);                     //更新
            ans+=t[1].sum2*(p[i+1].h-p[i].h);       //记录答案(当前被覆盖2次以上的长度乘上2条扫描线的差值)
        }
        printf("%.2lf\n",ans);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于LSTM的财务因子预测选股模型LSTM (Long Short-Term Memory) 是一种特殊的循环神经网络(RNN)架构,用于处理具有长期依赖关系的序列数据。传统的RNN在处理长序列时往往会遇到梯度消失或梯度爆炸的问题,导致无法有效地捕捉长期依赖。LSTM通过引入门控机制(Gating Mechanism)和记忆单元(Memory Cell)来克服这些问题。 以下是LSTM的基本结构和主要组件: 记忆单元(Memory Cell):记忆单元是LSTM的核心,用于存储长期信息。它像一个传送带一样,在整个链上运行,只有一些小的线性交互。信息很容易地在其上保持不变。 输入门(Input Gate):输入门决定了哪些新的信息会被加入到记忆单元中。它由当前时刻的输入和上一时刻的隐藏状态共同决定。 遗忘门(Forget Gate):遗忘门决定了哪些信息会从记忆单元中被丢弃或遗忘。它也由当前时刻的输入和上一时刻的隐藏状态共同决定。 输出门(Output Gate):输出门决定了哪些信息会从记忆单元中输出到当前时刻的隐藏状态中。同样地,它也由当前时刻的输入和上一时刻的隐藏状态共同决定。 LSTM的计算过程可以大致描述为: 通过遗忘门决定从记忆单元中丢弃哪些信息。 通过输入门决定哪些新的信息会被加入到记忆单元中。 更新记忆单元的状态。 通过输出门决定哪些信息会从记忆单元中输出到当前时刻的隐藏状态中。 由于LSTM能够有效地处理长期依赖关系,它在许多序列建模任务中都取得了很好的效果,如语音识别、文本生成、机器翻译、时序预测等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值