线段树 矩形周长

【USACO5.5.1】Picture矩形周长

Description

N(N<5000) 张矩形的海报,照片和其他同样形状的图片贴在墙上。它们的边都是垂直的或水平的。每个矩形可以部分或者全部覆盖其他矩形。所有的矩形组成的集合的轮廓称为周长。写一个程序计算周长。 
图 1 是一个有 7 个矩形的例子: 
 
图 1.一个 7 个矩形的集合 
对应的轮廓为图 2 所示的所有线段的集合: 
 
图 2. 矩形集合的轮廓 
所有矩形的顶点坐标均为整数。所有的坐标都在 [-10000,10000] 的范围内,并且任何一个矩形面积都为整数。结果的值可能需要 32 位有符号整数表示。

Input

第1行: N,张贴在墙上的矩形的数目。 第 2..N+1行 接下来的N行中,每行都有两个点的坐标,分别是矩形的左下角坐标和右上角坐标。每一个坐标由 X 坐标和 Y 坐标组成。

Output

只有一行,为一个非负整数,表示输入数据中所有矩形集合的轮廓长度。

Sample Input

7
-15 0 5 10
-5 8 20 25
15 -4 24 14
0 -6 16 4
2 15 10 22
30 10 36 20
34 0 40 16

Sample Output

228

分析:

1.每个矩形由两条横向边和两条纵向边组成。

2.对于横向边,按纵坐标排序。设当前讨论的边为 A [s , t]

    如果A是某个矩形的靠下的边,在树中查询[s,t]区间中被覆盖的长度为x,那么加上这条边后将增加

(t-s-x);

    如果A是某个矩形的靠上的边,先删除它的对应边,再在树中查询[s,t]区间中被覆盖的长度为x,那么加上这条边后将增加

(t-s-x);

3、对于纵向边,按横坐标排序,讨论方法与横向边相同。

注意建树方式是以单位线段为叶子节点

代码如下:

#include<cstdio> 
#include<iostream> 
#include<cstdlib> 
#include<cmath> 
#include<cstring> 
#include<queue> 
#include<vector> 
#include<algorithm> 
#define LL long long 
#define CLEAR(xxx) memset(xxx,0,sizeof(xxx)) 
using namespace std; 

const int maxn=20000+5,inf=1e9; 
int n,m,ans=0; 
inline void _read(int &x){ 
    char ch=getchar(); bool mark=false; 
    for(;!isdigit(ch);ch=getchar())if(ch=='-')mark=true; 
    for(x=0;isdigit(ch);ch=getchar())x=x*10+ch-'0'; 
    if(mark)x=-x; 
} 
struct segment{ 
    int s,t,p,id;  //s,t为线段的端点,p是排序的依据,id是排序前的编号。 
    segment(int a,int b,int c,int d):s(a),t(b),p(c),id(d){} 
    segment(){s=t=p=id=0;} 
    bool operator <(const segment a) const { 
    	return p<a.p; 
    } 
}; 
segment x[maxn],y[maxn]; 

int lazy[maxn<<4],cnt[maxn<<4],l,r;  
// cnt 表示区间被覆盖的次数,cnt[o]>0表示区间o被覆盖 

void pushdown(int o){ 
    int ls=o<<1,rs=ls+1; 
    lazy[ls]+=lazy[o]; 
    cnt[ls]+=lazy[o]; 
    cnt[rs]+=lazy[o]; 
    lazy[rs]+=lazy[o]; 
    lazy[o]=0;  
}  

void update(int o,int L,int R,int delta){ 
    if(lazy[o]) pushdown(o); 
    if(l<=L&&r>=R){ 
        cnt[o]+=delta; 
        lazy[o]+=delta; 
    } 
    else if(L+1<R){ 
        int mid=(L+R)>>1,ls=o<<1,rs=ls+1; 
        if(l<=mid&&L<=r) update(ls,L,mid,delta); 
        if(r>=mid&&l<=R)  update(rs,mid,R,delta); 
    } 
}  

int query(int o,int L,int R){ 
    if(lazy[o]) pushdown(o); 
    if(l<=L&&r>=R&&cnt[o]>0) return R-L; //被查询的区间完全包含,且被覆盖 
    if(L+1<R){ 
        int mid=(L+R)>>1,a=0,b=0; 
        if(l<=mid)a=query(o<<1,L,mid); 
        if(r>=mid)b=query(o*2+1,mid,R); 
        return a+b;  
    } 
    return 0;
}  
int main(){ 
    int i,j,x1,x2,y1,y2; 
    _read(n); 
    for(i=1;i<=n;i++){ 
        _read(x1); _read(y1) ;_read(x2) ;_read(y2); 
        x1+=10001; x2+=10001;     //注意将坐标全部变为正数 
        y1+=10001; y2+=10001; 
        x[i]=segment(x1,x2,y1,i); 
        y[i]  =segment(y1,y2,x1,i); 
        x[i+n]=segment(x1,x2,y2,i+n);    //编号为 n+1 ~ 2*n的是矩形的后一条边 
        y[i+n]  =segment(y1,y2,x2,i+n); 
    } 
    sort(x+1,x+1+2*n); 
    sort(y+1,y+1+2*n); 
    CLEAR(lazy); CLEAR(cnt);  
    for(i=1;i<=n*2;i++){      //讨论横向边 
        l=x[i].s; r=x[i].t; 
        if(x[i].id<=n){ 
            ans+=abs(r-l-query(1,1,maxn)); 
            update(1,1,maxn,1);  
        } 
        else { 
            update(1,1,maxn,-1); 
            ans+=abs(r-l-query(1,1,maxn) ); 
        }  
    } 
    CLEAR(lazy); CLEAR(cnt); 
    for(i=1;i<=n*2;i++){   //讨论纵向边 
        l=y[i].s; r=y[i].t;
        if(y[i].id<=n){ 
            ans+=abs(r-l-query(1,1,maxn) ); 
            update(1,1,maxn,1); 
        } 
        else { 
            update(1,1,maxn,-1); 
            ans+=abs(r-l-query(1,1,maxn)); 
        }  
    } 
    cout<<ans<<endl; 
    return 0; 
}



另一种写法:
#include<iostream> 
#include<cstdio> 
#include<algorithm> 
#include<cstring> 
#define inf 1e9 
using namespace std; 
const int maxn=50005; 
int n; 
struct node{ 
    int a,b; 
    int cover,lazy; 
};  
node tree[maxn*4]; 
struct line{ 
    int a,b,t,num; 
    line(){} 
    line(int whore,int postitute,int bastard,int tit){a=whore;b=postitute;t=bastard;num=tit;} 
}; 
line edgex[maxn*10],edgey[maxn*10]; 
bool cmp(line a,line b){ 
    return a.t<b.t; 
} 
void build_tree(int p,int x,int y){ 
    tree[p].a=x;tree[p].b=y; 
    tree[p].lazy=0;tree[p].cover=0; 
    int mid=(x+y)/2; 
    if(x+1<y){ 
        build_tree((p<<1),x,mid); 
        build_tree((p<<1)+1,mid,y); 
    } 
} 
void putdown(int p){ 
    //if(tree[p].a+1==tree[p].b)return; 
    int ls=(p<<1),rs=(p<<1)+1; 
    tree[ls].lazy+=tree[p].lazy; 
    tree[rs].lazy+=tree[p].lazy; 
    tree[ls].cover+=tree[p].lazy; 
    tree[rs].cover+=tree[p].lazy; 
    tree[p].lazy=0; 
} 
int query(int p,int x,int y){ 
    if(x>tree[p].b||y<tree[p].a)return 0; 
    if(tree[p].lazy)putdown(p); 
    if(x<=tree[p].a&&tree[p].b<=y&&tree[p].cover>0)return tree[p].b-tree[p].a; 
    if(tree[p].a+1<tree[p].b){ 
        int t1=query((p<<1),x,y); 
        int t2=query((p<<1)+1,x,y); 
        return t1+t2; 
    } 
    return 0; 
} 
void change(int p,int x,int y,int d){ 
    if(x>tree[p].b||y<tree[p].a)return; 
    if(tree[p].lazy)putdown(p); 
    if(x<=tree[p].a&&tree[p].b<=y){ 
        tree[p].lazy+=d; 
        tree[p].cover+=d; 
        return; 
    } 
    change((p<<1),x,y,d); 
    change((p<<1)+1,x,y,d); 
} 
int main(){ 
    int i,j,k,ans=0; 
    cin>>n; 
    for(i=1;i<=n;i++){ 
        int x1,y1,x2,y2; 
        scanf("%d%d%d%d",&x1,&y1,&x2,&y2); 
        x1+=10001;x2+=10001;y1+=10001;y2+=10001; 
        edgex[i]=line(x1,x2,y1,i); 
        edgex[i+n]=line(x1,x2,y2,i+n); 
        edgey[i]=line(y1,y2,x1,i); 
        edgey[i+n]=line(y1,y2,x2,i+n); 
    } 
    sort(edgex+1,edgex+1+2*n,cmp); 
    sort(edgey+1,edgey+1+2*n,cmp); 
    build_tree(1,1,20005); 
    int temp;
    for(i=1;i<=2*n;i++){ 
        if(edgex[i].num<=n){ 
        	temp=query(1,edgex[i].a,edgex[i].b);
            ans+=abs(edgex[i].b-edgex[i].a-temp); 
            change(1,edgex[i].a,edgex[i].b,1); 
        } 
        else{ 
        	temp=query(1,edgex[i].a,edgex[i].b);
            change(1,edgex[i].a,edgex[i].b,-1); 
            ans+=abs(edgex[i].b-edgex[i].a-temp); 
        } 
        //cout<<ans<<endl; 
    } 
    memset(tree,0,sizeof(tree)); 
    build_tree(1,1,20005); 
    for(i=1;i<=2*n;i++){ 
        if(edgey[i].num<=n){ 
        	temp=query(1,edgey[i].a,edgey[i].b);
            ans+=abs(edgey[i].b-edgey[i].a-temp); 
            change(1,edgey[i].a,edgey[i].b,1); 
        } 
        else{ 
        	temp=query(1,edgey[i].a,edgey[i].b);
            change(1,edgey[i].a,edgey[i].b,-1); 
            ans+=abs(edgey[i].b-edgey[i].a-temp); 
        } 
        //cout<<ans<<endl; 
    } 
    cout<<ans*2; 
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值