hdu 1542 1255 1828 线段树矩阵并面积 交面积 重叠周长解法

    我们先从hdu 1542开始http://acm.hdu.edu.cn/showproblem.php?pid=1542

    这里我们先把每个矩阵的点的y轴坐标,进行一个排序,然后通过排序以后的坐标建一颗线段树。对于每个条超元线段,我们给定一个cover值,表示插入了多少根线段。然后,在把所有与y轴平行的那些线段,我们给定一个flag,标记这是属于矩阵左边(1)还是右边(-1)的线段。从左往右插入线段树。访问到了一条超元线段时(node中的flag标记当前线段是否为超元线段),判断cover是否大于0,如果是,就计算面积,不是则加上线段的flag值。

    代码如下:

#include<iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
struct Line
{
    double x;
    double y1;
    double y2;
    int flag;
};
struct node
{
    double x;
    int c;
    double s;
    double e;
    int f;
    double len;
    int cover;
};
double yc[2000];
Line m[2000];
bool cmp(Line a,Line b)
{
    return a.x < b.x;
}
node tr[1000000];
void build_tree(int c,int s,int e)
{
    tr[c].x=-1;
    tr[c].s=yc[s];
    tr[c].e=yc[e];
    tr[c].len=yc[e]-yc[s];
    tr[c].f=0;
    tr[c].cover=0;
    if (s+1 >= e)
    {
        tr[c].f=1;
        return ;
    }
    build_tree(c<<1,s,(s+e)>>1);
    build_tree(c<<1 |1,(s+e)>>1,e);
    return ;
}
double update(int c,double x,double s,double e,int flag)
{
    if (e <= tr[c].s || s >= tr[c].e)
        return 0;
    if (tr[c].f == 1)
    {
        if (tr[c].cover > 0)
        {
            double t=tr[c].x;
            tr[c].x=x;
            tr[c].cover+=flag;
            return (x-t)*tr[c].len;
        }
        else
        {
            tr[c].cover+=flag;
            tr[c].x=x;
            return 0;
        }
    }
    return update(c<<1,x,s,e,flag)+update(c<<1 |1,x,s,e,flag);
}
int main()
{
    int n,lyc,i,k;
    double x1,x2,y1,y2,ans;
    k=0;
    while (1)
    {
        scanf("%d",&n);
        if (n == 0)
            break;
        lyc=0;
        for (i=0; i<n; i++)
        {
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            m[lyc].x=x1;
            m[lyc].y1=y1;
            m[lyc].y2=y2;
            m[lyc].flag=1;
            yc[lyc++]=y1;
            m[lyc].x=x2;
            m[lyc].y1=y1;
            m[lyc].y2=y2;
            m[lyc].flag=-1;
            yc[lyc++]=y2;
        }
        sort(yc,yc+lyc);
        sort(m,m+lyc,cmp);
        build_tree(1,0,lyc-1);
        ans=0;
        for (i=0; i<lyc; i++)
        {
            ans+=update(1,m[i].x,m[i].y1,m[i].y2,m[i].flag);
        }
         printf("Test case #%d\nTotal explored area: %.2f\n\n", ++k, ans);
    }
}


接下来的hdu 1255http://acm.hdu.edu.cn/showproblem.php?pid=1255

如果会求面积并的话,面积交就不是问题了。只需要将原先判断线段树中cover值为大于等于1改为大于等于2即可!

 

#include<iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
struct Line
{
    double x;
    double y1;
    double y2;
    int flag;
};
struct node
{
    double x;
    int c;
    double s;
    double e;
    int f;
    double len;
    int cover;
};
double yc[20000];
Line m[20000];
bool cmp(Line a,Line b)
{
    return a.x < b.x;
}
node tr[10000000];
void build_tree(int c,int s,int e)
{
    tr[c].x=-1;
    tr[c].s=yc[s];
    tr[c].e=yc[e];
    tr[c].len=yc[e]-yc[s];
    tr[c].f=0;
    tr[c].cover=0;
    if (s+1 >= e)
    {
        tr[c].f=1;
        return ;
    }
    build_tree(c<<1,s,(s+e)>>1);
    build_tree(c<<1 |1,(s+e)>>1,e);
    return ;
}
double update(int c,double x,double s,double e,int flag)
{
    if (e <= tr[c].s || s >= tr[c].e)
        return 0;
    if (tr[c].f == 1)
    {
        if (tr[c].cover >= 2)
        {
            double t=tr[c].x;
            tr[c].x=x;
            tr[c].cover+=flag;
            return (x-t)*tr[c].len;
        }
        else
        {
            tr[c].cover+=flag;
            tr[c].x=x;
            return 0;
        }
    }
    return update(c<<1,x,s,e,flag)+update(c<<1 |1,x,s,e,flag);
}
int main()
{
    int n,lyc,i,k,T;
    double x1,x2,y1,y2,ans;
    k=0;
    scanf("%d",&T);
    while (T--)
    {
        scanf("%d",&n);
        lyc=0;
        for (i=0; i<n; i++)
        {
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            m[lyc].x=x1;
            m[lyc].y1=y1;
            m[lyc].y2=y2;
            m[lyc].flag=1;
            yc[lyc++]=y1;
            m[lyc].x=x2;
            m[lyc].y1=y1;
            m[lyc].y2=y2;
            m[lyc].flag=-1;
            yc[lyc++]=y2;
        }
        sort(yc,yc+lyc);
        sort(m,m+lyc,cmp);
        build_tree(1,0,lyc-1);
        ans=0;
        for (i=0; i<lyc; i++)
        {
            ans+=update(1,m[i].x,m[i].y1,m[i].y2,m[i].flag);
        }
        printf("%.2f\n",ans);
    }
    return 0;
}


hdu 1828 http://acm.hdu.edu.cn/showproblem.php?pid=1828

分衡量和纵向两次分别求出横向部分的周长以及纵向部分的周长,然后相加就得到结果。

这道题数据比较弱,没有出两条线段覆盖的情况,所以这种解法能ac。

#include<iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
struct Line
{
    int x;
    int y1;
    int y2;
    int flag;
};
struct node
{
    int c;
    int s;
    int e;
    int f;
    int len;
    int cover;
};
int yc[20000],zg[20000];
Line m[20000],l[20000];
bool cmp(Line a,Line b)
{
    return a.x < b.x;
}
node tr[10000000];
void build_tree1(int c,int s,int e)
{
    tr[c].s=yc[s];
    tr[c].e=yc[e];
    tr[c].len=yc[e]-yc[s];
    tr[c].f=0;
    tr[c].cover=0;
    if (s+1 >= e)
    {
        tr[c].f=1;
        return ;
    }
    build_tree1(c<<1,s,(s+e)>>1);
    build_tree1(c<<1 |1,(s+e)>>1,e);
    return ;
}
void build_tree2(int c,int s,int e)
{
    tr[c].s=zg[s];
    tr[c].e=zg[e];
    tr[c].len=zg[e]-zg[s];
    tr[c].f=0;
    tr[c].cover=0;
    if (s+1 >= e)
    {
        tr[c].f=1;
        return ;
    }
    build_tree2(c<<1,s,(s+e)>>1);
    build_tree2(c<<1 |1,(s+e)>>1,e);
    return ;
}
double update(int c,double x,double s,double e,int flag)
{
    if (e <= tr[c].s || s >= tr[c].e)
        return 0;
    if (tr[c].f == 1)
    {
        if (tr[c].cover == 0 || tr[c].cover+flag == 0)
        {
            tr[c].cover+=flag;
            return tr[c].len;
        }
        else
        {
            tr[c].cover+=flag;
            return 0;
        }
    }
    return update(c<<1,x,s,e,flag)+update(c<<1 |1,x,s,e,flag);
}
int main()
{
    int n,lyc,i,k,T;
    int x1,x2,y1,y2,ans;
    while (scanf("%d",&n) != EOF)
    {
        lyc=0;
        for (i=0; i<n; i++)
        {
            scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
            m[lyc].x=x1;
            m[lyc].y1=y1;
            m[lyc].y2=y2;
            m[lyc].flag=1;
            l[lyc].y1=x1;
            l[lyc].y2=x2;
            l[lyc].x=y1;
            l[lyc].flag=1;
            zg[lyc]=x1;
            yc[lyc++]=y1;
            m[lyc].x=x2;
            m[lyc].y1=y1;
            m[lyc].y2=y2;
            m[lyc].flag=-1;
            l[lyc].y1=x1;
            l[lyc].y2=x2;
            l[lyc].x=y2;
            l[lyc].flag=-1;
            zg[lyc]=x2;
            yc[lyc++]=y2;
        }
        sort(yc,yc+lyc);
        sort(zg,zg+lyc);
        sort(l,l+lyc,cmp);
        sort(m,m+lyc,cmp);
        ans=0;
        build_tree1(1,0,lyc-1);
        for (i=0; i<lyc; i++)
        {
            ans+=update(1,m[i].x,m[i].y1,m[i].y2,m[i].flag);
        }
        build_tree2(1,0,lyc-1);
        for (i=0; i<lyc; i++)
        {
            ans+=update(1,l[i].x,l[i].y1,l[i].y2,l[i].flag);
        }
        printf("%d\n",ans);
    }
    return 0;
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值