区间合并在扫描线求周长中的应用---线段树+扫描线+离散化

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

 总结:此题与求覆盖后的面积类似,不同点就是在线段树节点中多了三个变量如下

struct node{
	ll l,r;//节点代表的区间 
	ll sum;//该区间被覆盖的长度 
	ll num;//该区间的线段的数目 
	ll mark;//Mark进行标记区间的覆盖情况 
	ll lp,rp;//被覆盖长度的左右端点 
}a[30000];

更新区间的时候更新num,sum和lp,rp,但是注意,当左孩子与右孩子之间被覆盖时,num要减一,如下:

void pushup(ll k){
	if(a[k].mark){//整段区间被覆盖时 
		a[k].sum=x[a[k].r+1]-x[a[k].l];
		a[k].num=1;//该区间只能是一条线段 
		a[k].lp=a[k].l;a[k].rp=a[k].r;//被覆盖的线段端点为该区间端点 
	}
	else if(a[k].l==a[k].r){//叶子节点且mark==0 
		a[k].num=a[k].sum=0;
		a[k].lp=a[k].rp=0;
	}
	else {//部分覆盖 
		a[k].sum=a[k<<1].sum+a[k<<1|1].sum;//用左孩子右孩子更新sum和num 
		a[k].num=a[k<<1].num+a[k<<1|1].num;
		a[k].lp=a[k<<1].lp;a[k].rp=a[k<<1|1].rp;//该区间所有线段的最左端和最右端 
		if(a[k<<1].rp+1==a[k<<1|1].lp){//左孩子,右孩子相连时 
			a[k].num--;
		}
	}
}

 求每部分的周长时,高很容易就能求了,那么底如何求呢,看下图:红色线代表a[k].sum,橙色线代表该区间的周长的底部分temp,可以看出temp=abs(上次a[1].sum-此次a[1].sum)

 

for(ll i=1;i<=cnt2;i++){
		ll l=lower_bound(x+1,x+1+k,line[i].l)-x;
		ll r=lower_bound(x+1,x+1+k,line[i].r)-x-1;
		update(1,l,r,line[i].flag);
		ll cnt=a[1].num;ll temp=abs(a[1].sum-last)
		ans+=temp;
		last=a[1].sum;
		if(i<cnt2){
			ans+=2*cnt*(line[i+1].h-line[i].h);
		}
	}

 

 

Problem Description

A number of rectangular posters, photographs and other pictures of the same shape are pasted on a wall.Their sides are all vertical or horizontal.Each rectangle can be partially or totally covered by the others.The length of the boundary of the union of all rectangles is called the perimeter.

Write a program to calculate the perimeter.An example with 7 rectangles is shown in Figure 1.
 



The corresponding boundary is the whole set of line segments drawn in Figure 2.
 

所有矩形的顶点都有整数坐标。
input:

你的程序是从标准输入中读取。第一行包含粘贴在墙上的矩形的数量。在随后的每一行中,都可以找到每个矩形的左下角顶点和右上角顶点的整数坐标。这些坐标的值以有序对的形式给出,由x坐标和y坐标组成。
0<=矩形数<5000。
所有坐标都在[-10000,10000]范围内,并且任何现有矩形都有一个正区域。
请处理到文件末尾。

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

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<vector>
#include<cmath>
#include<string>
#include<map>
#include<queue>
using namespace std;
typedef long long ll;
struct node{
	ll l,r;//节点代表的区间 
	ll sum;//该区间被覆盖的长度 
	ll num;//该区间的线段的数目 
	ll mark;//Mark进行标记区间的覆盖情况 
	ll lp,rp;//被覆盖长度的左右端点 
}a[30000];
struct seg{
	ll l,r,h,flag;
	seg(){
	}
	seg(ll l,ll r,ll h,ll flag):l(l),r(r),h(h),flag(flag){
	}
	bool operator < (const seg & c)const
	{
		return h<c.h;
	}
}line[20000];
ll x[20000];
void build(ll k,ll l,ll r){
	a[k].l=l;a[k].sum=a[k].num=a[k].mark=0;
	a[k].r=r;a[k].lp=0;a[k].rp=0;
	if(l==r)return ;
	ll mid=(a[k].l+a[k].r)>>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
	return ;
}
void pushup(ll k){
	if(a[k].mark){//整段区间被覆盖时 
		a[k].sum=x[a[k].r+1]-x[a[k].l];
		a[k].num=1;//该区间只能是一条线段 
		a[k].lp=a[k].l;a[k].rp=a[k].r;//被覆盖的线段端点为该区间端点 
	}
	else if(a[k].l==a[k].r){//叶子节点且mark==0 
		a[k].num=a[k].sum=0;
		a[k].lp=a[k].rp=0;
	}
	else {//部分覆盖 
		a[k].sum=a[k<<1].sum+a[k<<1|1].sum;//用左孩子右孩子更新sum和num 
		a[k].num=a[k<<1].num+a[k<<1|1].num;
		a[k].lp=a[k<<1].lp;a[k].rp=a[k<<1|1].rp;//该区间所有线段的最左端和最右端 
		if(a[k<<1].rp+1==a[k<<1|1].lp){//左孩子,右孩子相连时 
			a[k].num--;
		}
	}
}

void update(ll k,ll l,ll r,ll flag){
	if(a[k].l>=l&&a[k].r<=r){
		a[k].mark+=flag;
		pushup(k);
		return ;
	}
	ll mid=(a[k].l+a[k].r)>>1;
	if(l<=mid){
		update(k<<1,l,r,flag);
	}
	if(r>mid){
		update(k<<1|1,l,r,flag);
	}
	pushup(k);
}

int main(){
	ll n;
	while(cin>>n){
	ll x1,x2,y1,y2;
	ll cnt1=0,cnt2=0;
	for(ll i=1;i<=n;i++){
		cin>>x1>>y1>>x2>>y2;
		x[++cnt1]=x1;
		x[++cnt1]=x2;
		line[++cnt2]=seg(x1,x2,y1,1);
		line[++cnt2]=seg(x1,x2,y2,-1);	
	}
	sort(x+1,x+1+cnt1);
	sort(line+1,line+1+cnt2);
	ll k=unique(x+1,x+1+cnt1)-x-1;
	build(1,1,k-1);
	ll ans=0,last=0;
	for(ll i=1;i<=cnt2;i++){
		ll l=lower_bound(x+1,x+1+k,line[i].l)-x;
		ll r=lower_bound(x+1,x+1+k,line[i].r)-x-1;
		update(1,l,r,line[i].flag);
		ll cnt=a[1].num;ll temp=abs(a[1].sum-last)
		ans+=temp;
		last=a[1].sum;//存下上一次底边长 
		if(i<cnt2){//最后一条扫描线没有高 
			ans+=2*cnt*(line[i+1].h-line[i].h);
		}
	}
		cout<<ans<<endl;
	}
	return 0;
}
//            /\       |  /  |**、
//			 /  \      | /   |   \
//			/    \     |/    |   /  _____                      ____   |  /
//		   /------\    |\    |__/  /     \  \      /\      /  /    \  | /
//		  /        \   | \   |    /       \  \    /  \    /  /______\ |/
//		 /          \  |  \  |    \       /   \  /    \  /   \        |
//      /            \ |   \ |     \_____/     \/      \/     \_____  |
/**
 *        ┏┓    ┏┓
 *        ┏┛┗━━━━━━━┛┗━━━┓
 *        ┃       ┃  
 *        ┃   ━    ┃
 *        ┃ >   < ┃
 *        ┃       ┃
 *        ┃... ⌒ ...  ┃
 *        ┃       ┃
 *        ┗━┓   ┏━┛
 *          ┃   ┃ Code is far away from bug with the animal protecting          
 *          ┃   ┃   神兽保佑,代码无bug
 *          ┃   ┃           
 *          ┃   ┃        
 *          ┃   ┃
 *          ┃   ┃           
 *          ┃   ┗━━━┓
 *          ┃       ┣┓
 *          ┃       ┏┛
 *          ┗┓┓┏━┳┓┏┛
 *           ┃┫┫ ┃┫┫
 *           ┗┻┛ ┗┻┛
 */
// warm heart, wagging tail,and a smile just for you!
//
//                            _ooOoo_
//                           o8888888o
//                           88" . "88
//                           (| -_- |)
//                           O\  =  /O
//                        ____/`---'\____
//                      .'  \|     |//  `.
//                     /  \|||  :  |||//  \
//                    /  _||||| -:- |||||-  \
//                    |   | \\  -  /// |   |
//                    | \_|  ''\---/''  |   |
//                    \  .-\__  `-`  ___/-. /
//                  ___`. .'  /--.--\  `. . __
//               ."" '<  `.___\_<|>_/___.'  >'"".
//              | | :  `- \`.;`\ _ /`;.`/ - ` : | |
//              \  \ `-.   \_ __\ /__ _/   .-` /  /
//         ======`-.____`-.___\_____/___.-`____.-'======
//                            `=---='
//        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值