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

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1255

题目:可以参考hdu1542 http://blog.csdn.net/qq_36782366/article/details/75209119

思路:用sum数组记录重叠两次以上的边的长度  once数组记录重叠一次边的记录  方便于信息的向上传递

其他的与hdu1542一致,这里主要讲下这两个数组的使用

关于pushup函数的解释(重点)

void PushUp(int rt,int l,int r) {
	if (cnt[rt]>=2)
	{
		sum[rt] = X[r+1] - X[l];//需要理解
		once[rt]=0;
		return;
	}
	 if(cnt[rt]==1)
	{
		sum[rt] = once[rt<<1]+once[rt<<1|1]+sum[rt<<1]+sum[rt<<1|1];
		once[rt]=X[r+1] - X[l]-sum[rt];
		return ;
	}
	 if(cnt[rt]==0)
	{
		sum[rt] = sum[rt<<1] + sum[rt<<1|1];
		once[rt]=once[rt<<1]+once[rt<<1|1];
		return ;
	}
}

如果cntr【rt】等于2时,说明该区间被该区间长度的边覆盖了两层,所以对于sum(两层以上的的长度)就是该区间的长度,因为已经覆盖两层了,所以once长度即为0

如果cnt【rt】等于1时,说明该区间被该区间长度的边覆盖了一层,所以对于sum(两层以上的的长度)就是其子区间中覆盖一层和两层以上的长度和,对于once一层的长度为总的该区间长度减去两层以上的长度

如果cnt【rt】等于0时,说明该区间没有被该区间长度的边覆盖,所以对于sum(两层以上的的长度)就是其子区间中覆盖两层以上的长度和,对于once一层的长度即等于其子区间中覆盖一层的长度和


好了接下来直接上AC的代码

#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
const int maxn = 22022;
int cnt[maxn << 2];
double sum[maxn << 2];
double once[maxn<<2];
double X[maxn];
struct Seg
{
    double h , l , r;
    int s;
    Seg(){}
    Seg(double a,double b,double c,int d) : l(a) , r(b) , h(c) , s(d) {}
    bool operator < (const Seg &cmp) const
    {
         if( h==cmp.h)return s>cmp.s;
        return h < cmp.h;
    }
}ss[maxn];
void PushUp(int rt,int l,int r) {
    if (cnt[rt]>=2)
    {
        sum[rt] = X[r+1] - X[l];//需要理解
        once[rt]=0;
        return;
    }
     if(cnt[rt]==1)
    {
        sum[rt] = once[rt<<1]+once[rt<<1|1]+sum[rt<<1]+sum[rt<<1|1];
        once[rt]=X[r+1] - X[l]-sum[rt];
        return ;
    }
     if(cnt[rt]==0)
    {
        sum[rt] = sum[rt<<1] + sum[rt<<1|1];
        once[rt]=once[rt<<1]+once[rt<<1|1];
        return ;
    }
}
void update(int L,int R,int c,int l,int r,int rt)
{
    if (L <= l && r <= R)
    {
        cnt[rt] += c;//这个位置的边的条数  如果大于0时需要计入底边
        PushUp(rt , l , r);
        return ;
    }

    int m = (l + r) >> 1;
    if (L <= m)
        update(L , R , c , lson);
    if (m < R)
        update(L , R , c , rson);
    PushUp(rt , l , r);
}
int main()
{
    int n , cas;
    scanf("%d",&cas);
    while (cas--)
    {
        scanf("%d",&n) ;
        int m = 0;
        while (n --)
        {
            double a , b , c , d;
            scanf("%lf%lf%lf%lf",&a,&b,&c,&d);
            X[m] = a;
            ss[m++] = Seg(a , c , b , 1);
            X[m] = c;
            ss[m++] = Seg(a , c , d , -1);
        }
        sort(X , X + m);
        sort(ss , ss + m);
         int k=unique(X,X+m)-X;

        memset(cnt , 0 , sizeof(cnt));
        memset(sum , 0 , sizeof(sum));
        memset(once, 0 , sizeof(once));
        double ret = 0;
        for (int i = 0 ; i < m - 1 ; i ++)
        {
            int l=lower_bound(X,X+k,ss[i].l)-X;
            int r =lower_bound(X,X+k,ss[i].r)-X-1;
            if (l <= r) update(l , r , ss[i].s , 0 , k - 1, 1);//扫描线段时更新底边长度和底边相差个数
                ret += sum[1] * (ss[i+1].h - ss[i].h);//底乘以高
        }
        printf("%.2lf\n", ret);
    }
    return 0;
}






  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值