Hdu1828_Picture(线段树矩形周长并)

题意:

矩形周长并(轮廓线长度)

思路:

我们可以分开求,周长分为x轴上的和y轴上的

从下往上扫描,首先看x轴的边,第一条边我们可以直接加出贡献,如果第二条边我们和第一条有覆盖部分,那么我们要怎么加呢,我们会发现要加的也就是 ( 加入这条边后的有效长度和没加之前的有效长度的差值),只要加入一条边使得整个有效长度变化了(变大或变小),都说明它没有被其他边完全覆盖掉,它有一部分是露出来的,所以它对总周长是有贡献的。

Y轴同理,那就可以从下到上扫描一遍,从左向右再扫描一遍来做,这是第一种做法。

第二种就是只扫描一遍。从下往上扫描,X轴方向的边用上面的方法就可以,关键是如何在纵向扫描的同时统计出Y轴的信息。可以发现,纵向扫描的时候,矩形块被分成了很多“横条”,每个横条的高度好求(相邻边的高度差),关键是有几条是有效的周长,我们很容易直观的想到,最外面的肯定就是有效的周长,那么里面的呢?仔细一想可以知道,如果两个区间,3种情况如下:
在这里插入图片描述
两条绿线是相邻的扫描线划分成的横条,对号表示是有效周长,×表示不是。
只要判断两个区间的边界是否重合即可,用lbd和rbd分别来记录。

// 求矩形并的周长(轮廓线长度) 
#include <cstdio>
#include <cstring>
#include <algorithm>
#define lson l, mid, root<<1
#define rson mid, r, root<<1|1
using namespace std;
const int INF = 0x3f3f3f3f;
typedef long long LL;
const int maxn = 10000+5;

int X[maxn];
// 矩形的边 
struct Edge{
	int l, r, h;
	int flag;	// 上边还是下边 
	Edge(){}
	Edge(int a, int b, int c, int d):l(a),r(b),h(c),flag(d){}
	bool operator < (const Edge& rhs) const{
		return h < rhs.h;
	}
}edges[maxn];
// 线段树节点 
struct Node{
	int cover;	// 该区间被覆盖过几次
	int len;	// 区间内的有效长度
	int num;	// 区间内的竖线个数 
	bool lbd, rbd;
}Tree[maxn<<2];

void Stree_build(int l, int r, int root){
	Tree[root].cover = Tree[root].len = Tree[root].num = Tree[root].lbd = Tree[root].rbd = 0;
	if(l+1 == r) return;
	int mid = (l+r) >>  1;
	Stree_build(lson);
	Stree_build(rson);
}
void push_up(int l, int r, int rt){
	if(Tree[rt].cover > 0){
		Tree[rt].len = X[r] - X[l]; Tree[rt].num = 2;
		Tree[rt].lbd = Tree[rt].rbd = 1;
	}
	else if(l+1 == r){
		Tree[rt].len = Tree[rt].num = Tree[rt].lbd = Tree[rt].rbd = 0;
	}
	else{
		Tree[rt].len = Tree[rt<<1].len + Tree[rt<<1|1].len;
		Tree[rt].lbd = Tree[rt<<1].lbd; Tree[rt].rbd = Tree[rt<<1|1].rbd;
		Tree[rt].num = Tree[rt<<1].num + Tree[rt<<1|1].num - 2*(Tree[rt<<1].rbd&&Tree[rt<<1|1].lbd);
	}
}
void update(int la, int rb, int l, int r, int root, int val){
	if(la > r||rb < l) return;
	if(la <= l&&rb >= r){
		Tree[root].cover+= val;
		push_up(l, r, root);
		return;
	}
	if(l+1 == r) return;
	int mid = (l+r) >>  1;
	if(la < mid) update(la, rb, lson, val);
	if(rb > mid) update(la, rb, rson, val);
	push_up(l, r, root); 
}


int main()
{
	//freopen("in.txt","r",stdin);
	int n;
	int x1,y1,x2,y2;
	while(scanf("%d",&n) == 1&&n){
		int tot = 0;
		for(int i = 0; i < n; ++i){
			scanf("%d%d%d%d", &x1,&y1,&x2,&y2);
			edges[++tot] = Edge(x1, x2, y1, 1); X[tot] = x1;
			edges[++tot] = Edge(x1, x2, y2, -1); X[tot] = x2;
		}
		//printf("tot = %d\n", tot);
		sort(edges+1, edges+tot+1);
		sort(X+1, X+tot+1);
		
		int k = 1;
		for(int i = 2; i <= tot; ++i) if(X[i] != X[i-1]) X[++k] = X[i];
		
		Stree_build(1, k, 1);
		int ans = 0, last = 0;
		for(int i = 1; i <= tot; ++i){
			int l = lower_bound(X+1, X+k+1, edges[i].l) - X;
			int r = lower_bound(X+1, X+k+1, edges[i].r) - X;
			update(l, r, 1, k, 1, edges[i].flag);
			//横线:现在这次总区间被覆盖的程度和上一次总区间被覆盖的长度之差的绝对值
			ans += abs(Tree[1].len - last);
			//竖线:[下一条横线的高度-现在这条横线的高度]*num
			ans += (edges[i+1].h - edges[i].h) *Tree[1].num;
			//printf("last = %d\n", last);
			last = Tree[1].len;
		}
		printf("%d\n", ans);
	}

	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值