HDU - 1828 Picture

题目链接

题意 

多个矩形重叠在一起,求出轮廓线的长度。

 

分析 

把矩形分成横线和竖线来处理。现在分析横线的,竖线同理。矩形的坐标都是整数,且范围不大,故此题不需要离散化。从下往上扫描横线,每遇到一条横线,就计算长度,矩形的下边标为1,上边标为-1。具体计算方法是此次区间的总覆盖长度与上次区间长度相比,若没有变化,则说明新加入的横线是重叠的。故每次计算的答案便是两者之差的绝对值。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include <queue>
#include <vector>
#include<bitset>
#include<map>
#include<deque>
using namespace std;
typedef long long LL;
const int maxn = 10010;
const int mod = 77200211+233;
typedef pair<int,int> pii;
#define X first
#define Y second
#define pb push_back
//#define mp make_pair
#define ms(a,b) memset(a,b,sizeof(a))
const int inf = 0x3f3f3f3f;
#define lson l,m,2*rt
#define rson m+1,r,2*rt+1

struct edge{
    int l,r,h,f;
}ex[2*maxn],ey[maxn*2];

struct node{
    int l,r,cover,len;
    int mid(){
        return (l+r)>>1;
    }
}tree[maxn*8];

int cmp(edge a,edge b){
    return a.h<b.h;
}

void build(int l,int r,int rt){
    tree[rt].l=l,tree[rt].r=r;
    tree[rt].len=tree[rt].cover=0;
    if(l+1==r) return;
    int mid = tree[rt].mid();
    build(l,mid,rt<<1);
    build(mid,r,rt<<1|1);
}

void push_up(int rt){
    if(tree[rt].cover){
        tree[rt].len=tree[rt].r-tree[rt].l;
    }else if(tree[rt].l+1==tree[rt].r){
        tree[rt].len=0;
    }else{
        tree[rt].len=tree[rt<<1].len+tree[rt<<1|1].len;
    }
    return;
}

void update(edge e,int rt){
    if(tree[rt].l==e.l&&tree[rt].r==e.r){
        tree[rt].cover+=e.f;
        push_up(rt);
        return;
    }
    int m=tree[rt].mid();
    if(e.r<=m) update(e,rt<<1);
    else if(e.l>=m) update(e,rt<<1|1);
    else{
        edge temp = e;
        temp.r=m;
        update(temp,rt<<1);
        temp = e;
        temp.l=m;
        update(temp,rt<<1|1);
    }
    push_up(rt);
}
int ans;
void work(int l,int r,edge seg[],int n){
    build(l,r,1);
    int prelen=0;
    for(int i=0;i<n;i++){
        update(seg[i],1);
        ans += abs(tree[1].len-prelen);
        prelen=tree[1].len;
    }
}

int main(){
    int n,x1,x2,y1,y2;
    while(~scanf("%d",&n)){
        if(n==0){
            puts("0");
            continue;
        }
    
        int maxx,maxy,minx,miny;
        maxx=maxy=-inf,minx=miny=inf;
        int cnt=0;
        for(int i=0;i<n;i++){
            scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
            //横向
            ex[cnt].l=ex[cnt+1].l=x1,
            ex[cnt].r=ex[cnt+1].r=x2,
            ex[cnt].h=y1,ex[cnt+1].h=y2,
            ex[cnt].f=1,ex[cnt+1].f=-1;

            ey[cnt].l=ey[cnt+1].l=y1,
            ey[cnt].r=ey[cnt+1].r=y2,
            ey[cnt].h=x1,ey[cnt+1].h=x2,
            ey[cnt].f=1,ey[cnt+1].f=-1;
            cnt+=2;

            maxx=max(maxx,max(x1,x2));
            minx=min(minx,min(x1,x2));
            miny=min(miny,min(y1,y2));
            maxy=max(maxy,max(y1,y2));
        }

        sort(ex,ex+cnt,cmp);
        sort(ey,ey+cnt,cmp);
        ans=0;
        work(minx,maxx,ex,cnt);
        work(miny,maxy,ey,cnt);
        printf("%d\n",ans);
    }
    return 0;
}

 

还有一种更好的做好,参见http://www.cnblogs.com/scau20110726/archive/2013/04/13/3018687.html

同时计算横线和竖线

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 5010
#define MAX 10100
#define lch(i) ((i)<<1)
#define rch(i) ((i)<<1|1)

struct segment{
    int l,r,h,v;
}s[2*N];
struct node{
    int l,r,lp,rp,cnt,len,num;
    int mid()
    { return (l+r)>>1; }
}t[2*MAX*4];

int cmp(struct segment p ,struct segment q)
{
    return p.h<q.h;
}

void build(int l ,int r, int rt)
{
    t[rt].l=l; t[rt].r=r;
    t[rt].cnt=t[rt].len=0;
    t[rt].lp=t[rt].rp=t[rt].num=0;
    if(l==r) return ;
    int mid=t[rt].mid();
    build(l,mid,lch(rt));
    build(mid+1,r,rch(rt));
}

void cal(int rt)
{
    if(t[rt].cnt)
    {
        t[rt].len=t[rt].r-t[rt].l+1;
        t[rt].lp=t[rt].rp=1;
        t[rt].num=1;
    }
    else if(t[rt].l == t[rt].r) //叶子
    {
        t[rt].len=0;
        t[rt].lp=t[rt].rp=0;
        t[rt].num=0;
    }
    else
    {
        t[rt].len=t[lch(rt)].len+t[rch(rt)].len;
        t[rt].lp=t[lch(rt)].lp; 
        t[rt].rp=t[rch(rt)].rp;
        t[rt].num=t[lch(rt)].num + t[rch(rt)].num - (t[lch(rt)].rp&t[rch(rt)].lp);
    }
}

void updata(int l ,int r ,int v ,int rt)
{
    if(t[rt].l==l && t[rt].r==r) //目标区间
    {
        t[rt].cnt += v;
        cal(rt);
        return ;
    }
    int mid=t[rt].mid();
    if(r<=mid)     updata(l,r,v,lch(rt));
    else if(l>mid) updata(l,r,v,rch(rt));
    else
    {
        updata(l,mid,v,lch(rt));
        updata(mid+1,r,v,rch(rt));
    }
    cal(rt);
}

int main()
{
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        if(n==0) 
        { puts("0"); continue; }
        int i,maxx,minx,m;
        for(i=0,m=0,maxx=-MAX,minx=MAX; i<n; i++,m+=2)
        {
            int x1,y1,x2,y2;
            scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
            maxx=max(maxx,x2);
            minx=min(minx,x1);
            s[m].l=x1;   s[m].r=x2;   s[m].h=y1;   s[m].v=1;
            s[m+1].l=x1; s[m+1].r=x2; s[m+1].h=y2; s[m+1].v=-1;
        }
        sort(s,s+m,cmp);
        build(minx,maxx-1,1);
        int res=0 , prelen=0;
        s[m]=s[m+1]; //每次处理循环的最后一次
        for(int i=0; i<m; i++)
        {
            updata(s[i].l,s[i].r-1,s[i].v,1);
            res += abs(t[1].len-prelen); //计算横线部分
            res += (s[i+1].h-s[i].h)*(2*t[1].num); //计算竖线部分
            prelen=t[1].len;
        }
        printf("%d\n",res);
    }
    return 0;
}

 

posted @ 2018-03-26 19:43  litos  阅读(141)  评论(0编辑  收藏  举报