Hdu1828 Picture

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

大佬博客链接:点击打开链接

     用扫描线求周长,线段树的应用,以下代码给的是弱智的做法,虽然是参考并学习了大佬的博客但毕竟是我写的。分别计算一次x方向、y方向的周长,这题不需要离散化。要这样对横向线段进行操作:读入并记录每个矩形左右范围,以及y坐标,以及所有点所处的区间[l,r],这也是线段树建树的区间。类似于平时所见的(1,n)。然后按照y坐标从小到大对数组中每条线段进行更新,并且线段树要记录cnt值,即矩形的入边为1出边为-1,然后在push_up时按照cnt值判断。这里最关键的点是在线段树中叶子节点是(t,t)的意义其实是[t,t+1),也就是长度为1的线段(当且仅当在被覆盖时)。别的博客中也有提到,可以这样理解,对于一条线段[l,r],其实际上是覆盖了 l,l+1...,r-1这些个点,所以自然可以写出这样的域[l,r),左闭右开,这时对右端点实际上是无影响的,此时相当于已经把整个图形走完了。有关代码讲两个我思考了很久的地方,在update时因为是更新了线段树,所以自然r要减1才能符合我们上面的目的。另外一个地方就是push_up,虽然在学习大佬博客时我看出了这就是push_up,并且昨晚很无脑地套用了,因为感觉是对的。然而今天早上还是想了很久,当然前面实际上也说到了,在更新时有cnt值的叶子肯定是1,明显嘛r-l+1=1呀。但是没有覆盖就应该是0,所以要先判是否被覆盖,再判叶子,其他就把左右子树给加一加。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+7;
const int N=5007;
int ans;
struct node
{
    int l,r,h;
    int v;
    node(){}
    node(int a,int b,int c,int d):l(a),r(b),h(c),v(d){}
    bool operator <(const node& rhs)const
    {
        return h<rhs.h;
    }
}fx[N<<1],fy[N<<1];//
struct rw
{
    int l,r,cnt,len;
}tt[maxn<<2];
void build(int k,int l,int r)
{
    tt[k].l=l,tt[k].r=r,tt[k].cnt=tt[k].len=0;
    if(l==r) return;
    int mid=(l+r)>>1;
    build(k<<1,l,mid);
    build(k<<1|1,mid+1,r);
}
void push_up(int k)
{
    if(tt[k].cnt)
    {
        tt[k].len=tt[k].r-tt[k].l+1;
    }
    else if(tt[k].l==tt[k].r) tt[k].len=0;
    else tt[k].len=tt[k<<1].len+tt[k<<1|1].len;
}void update(int l,int r,int k,int v)
{
    if(tt[k].l>=l&&tt[k].r<=r)
    {
        tt[k].cnt+=v;
        push_up(k);
        return;
    }
    int mid=(tt[k].l+tt[k].r)>>1;
    if(l<=mid) update(l,r,k<<1,v);
    if(r>mid) update(l,r,k<<1|1,v);
    push_up(k);
}
void solve(int l,int r,struct node d[],int n)
{
    build(1,l,r-1);
    int tmp=0,pre=0;
    for(int i=0;i<n;i++)
    {
        update(d[i].l,d[i].r-1,1,d[i].v);
        tmp+=abs(tt[1].len-pre);
        pre=tt[1].len;
    }
    ans+=tmp;
}
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        if(n==0) break;
        int i=0,j=0;
        int maxx=-maxn,minx=maxn,maxy=-maxn,miny=maxn;
        for(;i<n;i++,j+=2)
        {
            int x1,y1,x2,y2;
            scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
            maxx=max(maxx,x2),minx=min(minx,x1);
            maxy=max(maxy,y2),miny=min(miny,y1);
            fx[j]=node(x1,x2,y1,1);
            fx[j+1]=node(x1,x2,y2,-1);
            fy[j]=node(y1,y2,x1,1);
            fy[j+1]=node(y1,y2,x2,-1);
        }
        sort(fx,fx+j);
        sort(fy,fy+j);
        ans=0;
        solve(minx,maxx,fx,j);
        solve(miny,maxy,fy,j);
        printf("%d\n",ans);

    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值