hdu 1828 / poj/pku 1177(Picture)(线段树求矩形覆盖面周长)

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


题意描述:题意大家都懂的


分析:此题求的方法和求面积并的方法是一样的,只是在线段树中增加了一些域方便求周长、此题的做法,因为这里的坐标是整点,如果你不采用离散化,那么你就将所有矩形平移到第一象限即每个坐标值+10000(这里线段树的最大范围就变为0,20000),若采用离散化y坐标的话,线段的最大范围为(0,10000);大体思路:先对每条线排序(按照x坐标从小到大排序),这里的线是形成矩形的竖直边,和对纵坐标排序,然后离散化纵坐标,之后从左到右的扫描排序之后的每条线,将每条线加入到线段树中,如果是入边我们则将这条被插入到线段树中,如果是出边我们则将条边从线段树中删除,在插入与删除的同时更新线段树; 以上都是废话,关键是线段树的维护,这里需要设置几个域,len表示区间覆盖的线段的总长度(那么每次向线段树中加入一条边无论是插入还是删除之后所得的新的总长度和没有加入这条边是的总长度不一致,那么这个变化量就是竖直方向周长的增加量,画图就很好理解),num表示该区间有几个不相交的线段(这里的不相交主要是处理两个竖直方向不相交的矩形的水平方向的周长,那么周长的增量就为 num*(x[i+1]-x[i])*2),lcover,rcover分别表示区间的左右端点是否被覆盖,若插入一条线段,那么这条线段所覆盖区间的左右端点都将会被覆盖,在回溯更新num的值的时候,若其左孩子区间的右端点和有孩子区间的左端点均已被覆盖那么说明tree[rt].num=tree[rt].num+tree[rt].num-1;,否则就不用减1啦,这样将排序后的先从左到右的扫描一遍就可以得出答案,注意最后一条边只有竖直方向上周长的变化,所以特殊处理一下即可!!


代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;


const int N=10005;
struct Line
{
    int x, y1,y2, id;
}a[N];


struct node
{
    int l,r;
    int cover;    //表示该区间被覆盖的次数
    int len;   //表示区间总的被覆盖的长度
    int num;      //表示区间中不相的线段的条数
    int rcover, lcover;
}tree[4*N];
int y[N];
bool cmp(Line x, Line y)
{
    return x.x < y.x;
}


void bulid (int rt, int l, int r)
{
    tree[rt].l=l;tree[rt].r=r;
    tree[rt].len=0;
    tree[rt].num=0;
    tree[rt].rcover=0;
    tree[rt].lcover=0;
    tree[rt].cover=0;
    if(l+1==r)return;
    int mid=(l+r)>>1;
    bulid(2*rt,l,mid);
    bulid(2*rt+1,mid,r);
}


int find(int x,int high)
{
    int low=0;
    while(low<=high)
    {
        int mid=(low+high)>>1;
        if(y[mid]==x)
        return mid;
        if(y[mid]>x)
        high=mid-1;
        else low=mid+1;
    }
}


void update(int rt, int l , int r, int side)
{
    if(tree[rt].l==l&&tree[rt].r==r)
        tree[rt].cover+=side;
    else
    {
    int mid=(tree[rt].l+tree[rt].r)>>1;
    if(r<=mid)update(2*rt,l,r,side);
    else if(l>=mid)update(2*rt+1,l,r,side);
    else {update(2*rt,l,mid,side);update(2*rt+1,mid,r,side);}
    }
        if(tree[rt].cover>0)
        {
            tree[rt].len=y[tree[rt].r]-y[tree[rt].l];
            tree[rt].lcover=tree[rt].rcover=1;
            tree[rt].num=1;
        }
        else if(tree[rt].l+1==tree[rt].r)
        {
            tree[rt].len=0;
            tree[rt].lcover=tree[rt].rcover=0;
            tree[rt].num=0;
        }
        else
        {
            tree[rt].len = tree[2*rt].len+tree[2*rt+1].len;
            tree[rt].lcover=tree[2*rt].lcover;
            tree[rt].rcover=tree[2*rt+1].rcover;
            tree[rt].num=tree[2*rt].num+tree[2*rt+1].num-tree[2*rt].rcover*tree[2*rt+1].lcover;
        }


}


void print(int rt)
{
    printf("%d %d %d %d\n",tree[rt].l,tree[rt].r, tree[rt].len, tree[rt].num);
    if(tree[rt].l+1==tree[rt].r)return;
    print(2*rt);
    print(2*rt+1);
}
int main ()
{
    int n,i,x1,y1,x2,y2;
    while(scanf("%d",&n)!=EOF)
    {
        int n1=0;
        for(i=0;i<n;i++)
        {
            scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
            y[n1]=y1; a[n1].x=x1; a[n1].y1=y1;a[n1].id=1;a[n1++].y2=y2;
            y[n1]=y2; a[n1].x=x2; a[n1].y1=y1;a[n1].id=-1;a[n1++].y2=y2;
        }
        sort(y,y+n1);
        sort(a,a+n1,cmp);
        int cnt;
        for(i=1,cnt=1;i<n1;i++)
        if(y[i]!=y[i-1])
        y[cnt++]=y[i];
        bulid(1,0,cnt-1);
        int res=0;
        int pre=0;
        int l, r;
        for(i=0;i<n1;i++)
        {
             l=find(a[i].y1,cnt-1);
             r=find(a[i].y2,cnt-1);
            update(1,l,r,a[i].id);
            res+=abs(tree[1].len-pre);         //纵向变化的长度为矩形边上的轮廓长度的部分
            if(i==n1-1)break;
            pre=tree[1].len;
            res+=tree[1].num*(a[i+1].x-a[i].x)*2; //横向的长度为几个不相交的线段数乘以横坐标的变化值
        }
        printf("%d\n",res);
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值