Picture POJ - 1177 扫描线(轮廓线)
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.
The vertices of all rectangles have integer coordinates.
Input
Your program is to read from standard input. The first line contains the number of rectangles pasted on the wall. In each of the subsequent lines, one can find the integer coordinates of the lower left vertex and the upper right vertex of each rectangle. The values of those coordinates are given as ordered pairs consisting of an x-coordinate followed by a y-coordinate.
0 <= number of rectangles < 5000
All coordinates are in the range [-10000,10000] and any existing rectangle has a positive area.
Output
Your program is to write to standard output. The output must contain a single line with a non-negative integer which corresponds to the perimeter for the input rectangles.
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
题意
题意其实非常明白 就是求所有矩形组成的不规则图形的边长总和是多少
题解
首先我们可以确定这道题要用扫描线去做 但建议先学习扫描线求总面积 然后再来学习求周长 毕竟求周长相比于求面积还是要更复杂一点
当我们明白了求面积以后 我们来思考下与求周长有什么区别 首先我们求面积线段树上只需要维护一个区间被覆盖的长度即可 这样总面积就相当于每两个扫描线的高度差与区间被覆盖的长度的乘积 求周长呢 我们不仅需要与扫描线平行的那条边 也需要与扫描线垂直的那两条边 这样一说的话我们的任务便成了两个 一个是计算每次扫描线扫过后平行的边需要增加多少 一个是垂直的边需要几条 有可能出现样例中中空的情况 这样在扫描线扫描我们其实需要的是2N条高 那N是多少呢 其实就是我们线段树要维护的值 区间连续段的多少 这样一想 与扫描线垂直的边每次增加的值不就是
(h2-h1)×2×segt[1].sum(区间连续段数量) 那么与扫描线平行的的边每次增加的值其实就是abs(旧的区间被覆盖的值-新的区间被覆盖的值) 至于为什么是绝对值的原因是因为遇到上界的时候相减的值其实是负的 可以动手看看 这样我们的问题就解决了 线段树上维护两个值 一个是区间连续段的长度 一个是区间和 每次增加两个方向的值 这样就ok了 剩下的就是扫描线的日常离散化操作了 在查找的时候二分下就好 其实感觉数据量不大 搜一遍也不是不可以 但lower_bound那么好用 没有不用的道理呀
记得一种特殊情况 就是两个矩形有一个边重合时 我们其实是不计算中间的两个边的 这样我们只需要在排序是把下界排在上界之前即可
这是一组样例
3
1 1 10 10
10 1 20 10
20 1 30 10
可以体会下这个样例
以下是AC代码
//轮廓线 可以当做板子 (其中线段树维护一个区间连续段的长度和区间和)
#include<iostream>
#include<algorithm>
#include<string>
#include<map>
#include<set>
#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<stack>
#include<vector>
#include<cstdlib>
#include<utility>
#include<cstring>
#include<cstdio>
using namespace std;
#define lson (q<<1)
#define rson (q<<1|1)
#define mid ((l+r)>>1)
const int maxn = 10000+10;
struct line{
int y1,y2;
int h,flag;
line() = default;
line(int yy1,int yy2,int hh,int f):y1(yy1),y2(yy2),h(hh),flag(f){}
bool operator<(const line & a)const{
if(h == a.h) return flag > a.flag; //考虑到边重合的情况 需要入边在最前面
return h < a.h;
}
};
struct node{
int len;//区间被覆盖的长度
int sum;//区间的不连续线段数
int lazy;
bool lr,rr;//左右边界是否被覆盖
};
node segt[maxn<<2];
line book[maxn];
int x1,x2,yy1,y2,top,ans;
int n;
vector<int> vec;
void init(){
memset(segt,0,sizeof(segt));
memset(book,0,sizeof(book));
vec.clear();
ans = 0,top = 0;
}
void push_up(int q,int l,int r){
if(segt[q].lazy > 0){//这很重要 lazy标记是不用修改的
segt[q].len = vec[r+1] - vec[l];
segt[q].lr = segt[q].rr = true;
segt[q].sum = 1;
return;
}
if(l == r){
segt[q].len = segt[q].sum = segt[q].lazy= 0;
segt[q].rr = segt[q].lr = false;
return;
}
segt[q].len = segt[lson].len + segt[rson].len;
segt[q].sum = segt[lson].sum + segt[rson].sum - (segt[lson].rr & segt[rson].lr);
segt[q].lr = segt[lson].lr;
segt[q].rr = segt[rson].rr;
}
void update(int q,int l,int r,int lhs,int rhs,int k){
if(l>=lhs && r<=rhs){
segt[q].lazy += k;
push_up(q,l,r);
return;
}
int m = mid;
if(lhs <= m) update(lson,l,m,lhs,rhs,k);
if(rhs > m) update(rson,m+1,r,lhs,rhs,k);
push_up(q,l,r);
}
int main(){
while(cin >> n){
init();
for(int i=0;i<n;++i){
cin >> x1 >> yy1 >> x2 >> y2;
vec.push_back(yy1);
book[top++] = {yy1,y2,x1,1};
vec.push_back(y2);
book[top++] = {yy1,y2,x2,-1};
}
sort(vec.begin(),vec.end());
sort(book,book+top);
vec.erase(unique(vec.begin(),vec.end()),vec.end());
std::size_t len = vec.size();
int lines = 0,Old_Len = 0;
for(int i=0;i<top;++i){
int l = lower_bound(vec.begin(),vec.end(),book[i].y1) - vec.begin();
int r = lower_bound(vec.begin(),vec.end(),book[i].y2) - vec.begin() - 1;
update(1,0,len-1,l,r,book[i].flag);
if(i>=1) ans+=(lines*2*(book[i].h-book[i-1].h));
ans += abs(segt[1].len - Old_Len);
Old_Len = segt[1].len;
lines = segt[1].sum;
}
cout << ans << endl;
}
return 0;
}