题意:有n个矩形,每个矩形给出它的左下角和右上角的两个点的坐标,要求所有n个矩形在平面中组成的图形的总周长。
分析:用线段树记录坐标系上,与y轴平行的方向覆盖的长度和连续的区间个数。记录矩形的每一条垂直(与y轴平行)的边,按照x从小到大排序。左边的边加入线段树,右边的边移除线段树,计算总周长。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define ld d<<1
#define rd d<<1|1
const int N = 5005;
struct seg
{
int y1,y2,x,flag; //记录与y轴平行的边,y1、y2为边的两个端端点的y坐标
}a[N << 2]; //flag == 1表示为左边的边
struct node
{
int l,r,lbd,rbd,num,len,count; //线段树的节点,num为区间内有几个不连续的线段,len为该区间内覆盖线段的总长度
}tr[N<<4]; //lbd,rbd标记左右端点是否被覆盖
int y[N<<2];
bool cmp(seg a, seg b)
{
if(a.x == b.x) return a.flag > b.flag;
return a.x < b.x;
}
void build(int d, int l, int r)
{
tr[d].l = l,tr[d].r = r;
tr[d].lbd = tr[d].rbd = tr[d].num = tr[d].len = tr[d].count = 0;
if(l == r - 1) return; //因为每个节点都代表一个线段,所以不存在l == r 的情况,此处就应该退出
int m = (l+r) >>1;
build(ld,l,m); build(rd,m,r);
}
void update_len(int d, int l, int r)
{
if(tr[d].count > 0) tr[d].len = y[r] - y[l];
else if(r - l ==1) tr[d].len = 0;
else
{
tr[d].len = tr[ld].len + tr[rd].len;
}
}
void update_num(int d, int l, int r)
{
if(tr[d].count > 0)
{
tr[d].lbd = tr[d].rbd = tr[d].num = 1;
}
else if(r == l + 1) tr[d].lbd = tr[d].rbd = tr[d].num = 0;
else
{
tr[d].lbd = tr[ld].lbd; tr[d].rbd = tr[rd].rbd;
tr[d].num = tr[ld].num + tr[rd].num - tr[ld].rbd*tr[rd].lbd;
}
}
void solve(int d, int l, int r, int ql, int qr,int fl)
{
if(ql <= y[l] && y[r] <= qr)
{
tr[d].count += fl;
}
else if(l == r - 1) return;
else
{
int m = (l+r) >> 1;
if(ql <= y[m]) solve(ld,l,m,ql,qr,fl); //注意这里的左儿子的区间是(l,m),右儿子的区间是(m,r)因为每个节点代表的是坐标系上的一个线段
if(y[m] < qr) solve(rd,m,r,ql,qr,fl);
}
update_len(d,l,r); update_num(d,l,r);
}
int main()
{
int n;
while(~scanf("%d",&n))
{
int x1,x2,y1,y2;
int k = 0;
for(int i = 0; i < n; i++)
{
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
a[k].x = x1, a[k].y1 = y1, a[k].y2 = y2, a[k].flag = 1; y[k++] = y1;
a[k].x = x2, a[k].y1 = y1, a[k].y2 = y2, a[k].flag = 0; y[k++] = y2;
}
sort(a,a+k,cmp);
sort(y,y+k);
int cony = unique(y,y+k) - y; // 将y坐标离散化
build(1,0,cony - 1);
int ans = 0,nnum = 0,nlen = 0;
for(int i = 0; i < k; i++)
{
if(a[i].flag) solve(1,0,cony - 1,a[i].y1,a[i].y2,1);
else solve(1,0,cony - 1,a[i].y1,a[i].y2,-1);
if(i >= 1)
ans += 2*nnum*(a[i].x - a[i-1].x); //计算新增边后与x轴平行的方向增加的周长
ans += abs(tr[1].len - nlen); //与y轴平行方向增加的周长
nnum = tr[1].num; nlen = tr[1].len;
}
printf("%d\n",ans);
}
}