做这道题之前,建议先做POJ 1151  Atlantis,经典的扫描线求矩阵的面积并

参考连接:

http://www.cnblogs.com/scau20110726/archive/2013/04/13/3018702.html

线段树辅助——扫描线法计算矩形周长并(轮廓线):
http://www.cnblogs.com/scau20110726/archive/2013/04/13/3018687.html
http://blog.csdn.net/ophunter/article/details/9129557

题意:求几个矩形并后的周长

思路:这和求矩形并的面积差不多,也是要用到扫描线,一根一根地扫描。只不过这里比求面积的要麻烦点。
     离散化就不用说了,具体说下如何求周长吧。
     首先将横线的信息存起来,按照y从小到大排序,当y相同时,矩阵的下边界排在前面,上边界排在后面,具体原因后面会有说明。
   然后一条一条的开始插入,计算周长分为两种:
   1.计算垂直方向的周长时,首先我们要知道整个区间被分成了多少段,即该区间被多少条线段覆盖,设为m,
    那么垂直方向的增量即为2*m*(y2-y1),(y2-y1)即为当前扫描线的纵坐标和前一次纵坐标的差值。
   2.计算水平方向的周长时,统计当前区间的总长度,同时记录上一个状态的时候区间总长度,那么这2次区间总长度之差的绝对值
    就是插入当前扫描线后,总区间内水平方向的增量。

   
    不过这里有一点要注意的是:会有重边的出现。

    

      

  当纵坐标一样时,为了防止矩阵A的上边界和矩阵B的下边界重合时,实际上增量为0。
  但如果先加入矩阵A的上边界l1,即先删除,当前区间总长度为绿色的线条1,现在变为蓝色的线条2,增量为某值a;
  再加入矩阵B的下边界l2,上一次区间总长度为线条2,现在变为紫色的线条3,增量又为a,这样导致多计算了2*a。
  所以正确的是应该先扫描矩阵B的下边界(tp=1),再扫描矩阵A的上边界(tp=-1),即y相同时,tp=1的排在前面。即先插入,再删除。
  这样先插入l2,区间总长度不变,再插入l1,区间总长度仍不变,这样增量就为0.

  不过测试的数据里没有出现重边的情况,所以AC的方法并不一定是正确的。

我线段树建立的叶子节点是区间(a,a+1),而不是点,这样是为了方便求区间长度。
  更新的时候,是直接更新到叶子节点的。
  本想在更新时,用lazy标记,这样不必更新到叶子节点,但是样例一直不过。
  后来调试时候,发现当更新某一个节点时,该节点cnt=1,但是父节点的cnt仍是0。
  这样如果某次更新正好更新到父节点时,cnt为0,但实际上父节点中所包含的区间中有部分cnt=1,这样就导致更新的时候会不正确。
  因此正确的更新还是要更新到叶子节点。

  最后还要说明一下的是:POJ 上面只有一组数据,而HDU 上面有多组数据。

附上代码:

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <math.h>
#define lson rt<<1,L,mid
#define rson rt<<1|1,mid,R using namespace std;
const int maxn=;
int n,cnt=; //n为矩形的个数,cnt为离散后的点x的个数
int hashval[maxn]; //x坐标对应的离散的值
int xval[maxn]; //存储所有的x坐标的值
int idx=; //xval存储的数的个数 struct Line {
int l,r,y; //l:左端点 r:右端点 y:纵坐标
int tp; //标记,1为矩形的下边界,-1为矩形的上边界
bool operator<(const Line tmp)const {
/*
当纵坐标一样时,矩阵的下边界(tp=1)排在前面,矩阵的上边界(tp=-1)排在后面
*/
if(y==tmp.y)
return tp>tmp.tp;
return y<tmp.y;
}
} line[maxn];
int lnum=; struct Node {
int lp,rp; //标记左右端点是否被线条覆盖,1为是,0为否,用于在pushUp时,统计父亲的num值
int cnt; //表示这个区间被覆盖的次数
int len; //这个区间被覆盖的长度
int num; //该区间被多少条线段覆盖
} tree[maxn<<]; //二分搜索离散后的值
int binarySearch(int m) {
int l=,r=cnt,mid;
while(r>=l) {
mid=(l+r)>>;
if(hashval[mid]==m)
return mid;
if(hashval[mid]<m)
l=mid+;
else
r=mid-;
}
} void pushUp(int rt) {
tree[rt].lp=tree[rt<<].lp;
tree[rt].rp=tree[rt<<|].rp;
tree[rt].num=tree[rt<<].num+tree[rt<<|].num;
tree[rt].len=tree[rt<<].len+tree[rt<<|].len;
if(tree[rt<<].rp== && tree[rt<<|].lp==)
tree[rt].num--;
}
void build(int rt,int L,int R) {
tree[rt].cnt=tree[rt].lp=tree[rt].rp=tree[rt].num=tree[rt].len=;
if(L+==R)
return;
int mid=(L+R)>>;
build(lson);
build(rson);
} void update(int rt,int L,int R,int l,int r,int p) {
if(l<=L&&R<=r) {
tree[rt].cnt+=p;
if(tree[rt].cnt) {
tree[rt].lp=tree[rt].rp=;
tree[rt].num=;
tree[rt].len=hashval[R]-hashval[L];
} else {
tree[rt].lp=tree[rt].rp=;
tree[rt].num=tree[rt].len=;
}
return;
}
int mid=(L+R)>>;
if(l<mid)
update(lson,l,r,p);
if(r>mid)
update(rson,l,r,p);
pushUp(rt);
} int main() {
int x1,y1,x2,y2;
while(scanf("%d",&n)!=EOF) {
cnt=idx=;
for(int i=; i<=n; i++) {
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
line[*i-].l=x1;line[*i-].r=x2;line[*i-].y=y1;line[*i-].tp=;
line[*i].l=x1;line[*i].r=x2;line[*i].y=y2;line[*i].tp=-;
xval[++idx]=x1;
xval[++idx]=x2;
}
lnum=*n;
sort(line+,line+lnum+);
sort(xval+,xval+idx+);
//对x坐标进行离散
hashval[++cnt]=xval[];
for(int i=; i<=idx; i++) {
if(xval[i]!=xval[i-]) {
hashval[++cnt]=xval[i];
}
}
build(,,cnt);
long long ans=;
int last=; //last记录插入上一次扫描线的区间总长度
int x,y;
for(int i=; i<=lnum; i++) {
ans+=tree[].num**(line[i].y-line[i-].y);
x=line[i].l;
y=line[i].r;
x=binarySearch(x);
y=binarySearch(y);
for(int j=x; j<=y-; j++)
update(,,cnt,j,j+,line[i].tp);
ans+=abs(tree[].len-last);
last=tree[].len;
}
printf("%I64d\n",ans);
}
return ;
}

后来写了个区间更新的,代码贴上:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#define lson rt<<1,L,mid
#define rson rt<<1|1,mid+1,R
/*
区间更新AC 15ms
把之前写的单点更新的提交了下,0ms。。。
区间更新反而比单点更新慢。。。郁闷。。。 这次建立的叶子节点是(a,a),所以在二分查找对应的区间(a,b)时,右端点b要减1
*/
using namespace std;
const int maxn=+;
int n,cnt; int hashx[maxn<<];
struct Node{
int cnt;
int lp,rp;
int num;
int len;
}tree[maxn<<]; struct Line{
int l,r,y;
int tp;
bool operator<(const Line tmp)const{
if(y==tmp.y){
return tp>tmp.tp;
}
else
return y<tmp.y;
}
}line[maxn<<]; void build(int rt,int L,int R){
tree[rt].cnt=tree[rt].lp=tree[rt].rp=tree[rt].num=;
tree[rt].len=;
if(L==R)
return;
int mid=(L+R)>>;
build(lson);
build(rson);
} void pushUp(int rt,int L,int R){
if(tree[rt].cnt){
tree[rt].len=hashx[R+]-hashx[L];
tree[rt].lp=tree[rt].rp=;
tree[rt].num=;
}
else{
if(L==R){
tree[rt].len=;
tree[rt].lp=tree[rt].rp=tree[rt].num=;
}
else{
//父节点cnt=0,但可能子节点有cnt不为0的,所以父亲要从子节点处获得更新
tree[rt].lp=tree[rt<<].lp;
tree[rt].rp=tree[rt<<|].rp;
tree[rt].len=tree[rt<<].len+tree[rt<<|].len;
tree[rt].num=tree[rt<<].num+tree[rt<<|].num;
if(tree[rt<<].rp && tree[rt<<|].lp)
tree[rt].num--; //减去一个重复计算的
}
}
} void update(int rt,int L,int R,int l,int r,int val){
if(l<=L&&R<=r){
tree[rt].cnt+=val;
pushUp(rt,L,R); //区间更新这里不能忘
return;
}
int mid=(L+R)>>;
if(l<=mid)
update(lson,l,r,val);
if(r>mid)
update(rson,l,r,val);
pushUp(rt,L,R);
} int binarySearch(int x,int n){
int l=,r=n+,mid;
while(r-l>){
mid=(l+r)>>;
if(hashx[mid]<=x)
l=mid;
else
r=mid;
}
return l;
} int main()
{
int x1,x2,y1,y2;
while(scanf("%d",&n)!=EOF){
cnt=;
for(int i=;i<=n;i++){
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
line[*i-].y=y1;line[*i-].l=x1;line[*i-].r=x2;line[*i-].tp=;
line[*i].y=y2;line[*i].l=x1;line[*i].r=x2;line[*i].tp=-;
hashx[cnt++]=x1;
hashx[cnt++]=x2;
}
n=n*;
sort(hashx+,hashx+cnt);
sort(line+,line+n+);
int idx=;
for(int i=;i<=n;i++){
if(hashx[i]!=hashx[i-])
hashx[++idx]=hashx[i];
}
build(,,idx);
long long ans=;
int last=;
for(int i=;i<=n;i++){
ans+=(line[i].y-line[i-].y)**tree[].num;
int a=binarySearch(line[i].l,idx);
int b=binarySearch(line[i].r,idx)-;
update(,,idx,a,b,line[i].tp);
ans+=abs(tree[].len-last);
last=tree[].len;
}
printf("%I64d\n",ans);
}
return ;
}

HDU 1828 / POJ 1177 Picture (线段树扫描线,求矩阵并的周长,经典题)的更多相关文章

  1. poj 1177 --- Picture(线段树+扫描线 求矩形并的周长)

    题目链接 Description A number of rectangular posters, photographs and other pictures of the same shape a ...

  2. HDU 1828 / POJ 1177 Picture --线段树求矩形周长并

    题意:给n个矩形,求矩形周长并 解法:跟求矩形面积并差不多,不过线段树节点记录的为: len: 此区间线段长度 cover: 此区间是否被整个覆盖 lmark,rmark: 此区间左右端点是否被覆盖 ...

  3. POJ 1177 Picture(线段树 扫描线 离散化 求矩形并面积)

    题目原网址:http://poj.org/problem?id=1177 题目中文翻译: 解题思路: 总体思路: 1.沿X轴离散化建树 2.按Y值从小到大排序平行与X轴的边,然后顺序处理 如果遇到矩形 ...

  4. poj 1177 Picture (线段树 扫描线 离散化 矩形周长并)

    题目链接 题意:给出n个矩形,每个矩形给左下 和 右上的坐标,求围成的周长的长度. 分析: 首先感谢大神的博客,最近做题经常看大神的博客:http://www.cnblogs.com/kuangbin ...

  5. hdu 1542&&poj 1151 Atlantis[线段树+扫描线求矩形面积的并]

    Atlantis Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total S ...

  6. hdu1828 线段树扫描线求矩形面积的周长

    题意:       给你n个矩形,问你这n个矩形所围成的图形的周长是多少. 思路:       线段树的扫描线简单应用,这个题目我用的方法比较笨,就是扫描两次,上下扫描,求出多边形的上下边长和,然后同 ...

  7. Picture POJ - 1177 (线段树-扫描线)

    A number of rectangular posters, photographs and other pictures of the same shape are pasted on a wa ...

  8. HDU 1828 POJ 1177 Picture

    矩形周长并 POJ上C++,G++都能过,HDU上C++过了,G++WA ,不知道为什么 #include<cstdio> #include<cstring> #include ...

  9. POJ 1177 Picture(线段树周长并)

      描述 A number of rectangular posters, photographs and other pictures of the same shape are pasted on ...

随机推荐

  1. Box of Bricks最小移动砖块数目

    Description Little Bob likes playing with his box of bricks. He puts the bricks one upon another and ...

  2. ED/EP系列6《扩展应用》

    包括:电子钱包复合应用:电子钱包灰锁应用. 1. 复合应用模式 Ø INITIALIZE FOR CAPP PURCHASE(复合应用消费初始化): Ø UPDATE CAPP DATA CACHE( ...

  3. Cron表达式说明

    CronTrigger CronTriggers往往比SimpleTrigger更有用,如果您需要基于日历的概念,而非SimpleTrigger完全指定的时间间隔,复发的发射工作的时间表. CronT ...

  4. 使用Handler和Timer+Timertask实现简单的图片轮播

    布局文件就只放了一个简单的ImageView,就不展示了. 下面是Activity package com.example.administrator.handlerthreadmessagedemo ...

  5. hdu 4027 Can you answer these queries?

    题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=4027 Can you answer these queries? Description Proble ...

  6. 微信扫码支付asp.net(C#)实现步骤

    支付提交页面: [HttpPost] public ActionResult index(decimal amount) { //生成订单10位序列号,此处用时间和随机数生成,商户根据自己调整,保证唯 ...

  7. com.Goods.ForEach

    com.Goods.ForEach(g => { g.TransactionPrice = getUnitPriceByProductId(g.ProductID); g.ExpressMone ...

  8. MYSQL procedure

    没怎么接触过mysql procedure,今天建个calendar表还磨磨唧唧的,记录一下: CREATE PROCEDURE `new_procedure` (start_date DATA,en ...

  9. QT 按钮类继承处理带定时器

    01.class KeyButton : public QPushButton  02.{  03.    Q_OBJECT  04.public:  05.    explicit KeyButto ...

  10. licens 问题 Error (292028): Specified license is not valid for this machine

    集成网卡调试的时候坏了,造成了quartus 不可以用,MAC地址不对应了... 应该怎么解决呢??.