详解:算法总结:【线段树+扫描线】&矩形覆盖求面积/周长问题(HDU 1542/HDU 1828)
参考了大佬的思路
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#define ls rt<<1
#define rs rt<<1|1
#define mid() (p[rt].l+p[rt].r)>>1
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=5010;
struct edge
{
int l,r; //左右端点
int h; //高
int v; //是上边还是下边
}e[maxn<<2];
bool cmp(edge a,edge b)
{
return a.h<b.h;
}
struct node
{
int l,r; //该节点代表的线段的左右端点坐标
int len; //这个区间被覆盖的长度
int s; //表示这个区间被重复覆盖了几次
bool lc,rc; //表示这个节点左右两个端点是否被覆盖(0表示没有被覆盖,1表示有被覆盖)
int num; //这个区间有多少条线段(这个区间被多少条线段覆盖)
//len用来计算横线 num用来计算竖线
}p[maxn<<4];
void build(int l,int r,int rt)
{
p[rt].l = l , p[rt].r = r;
p[rt].lc = p[rt].rc = 0;
p[rt].num = p[rt].len = p[rt].s = 0;
if(l==r)
return ;
int m=mid();
build(l,m,ls);
build(m+1,r,rs);
}
void pushup(int rt) //区间合并
{
if(p[rt].s) //完全覆盖
{
p[rt].len = p[rt].r-p[rt].l+1;
p[rt].num = 1;
p[rt].lc = p[rt].rc = 1;
}
else if(p[rt].l==p[rt].r) //是一个点
{
p[rt].len = 0;
p[rt].num = 0;
p[rt].lc = p[rt].rc = 0;
}
else //根据左右子节点更新这个点
{
p[rt].len = p[ls].len+p[rs].len;
//和左儿子共左端点,和右儿子共右端点
p[rt].lc = p[ls].lc;
p[rt].rc = p[rs].rc;
p[rt].num = p[ls].num+p[rs].num-(p[ls].rc&p[rs].lc); //如果左子的右端点和右子的左端点都被覆盖了
}
}
void update(int l,int r,int rt,int f)
{
if(p[rt].l==l && p[rt].r==r)
{
p[rt].s += f;
pushup(rt);
return ;
}
int m = mid();
if(l>m)
update(l,r,rs,f);
else if(r<=m)
update(l,r,ls,f);
else
{
update(l,m,ls,f);
update(m+1,r,rs,f);
}
pushup(rt);
}
int main()
{
int n;
int tot=0;
int x1,y1,x2,y2;
int mn=inf;
int mx=-inf;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
mn=min(mn,min(x1,x2));
mx=max(mx,max(x1,x2));
e[tot].l = e[tot+1].l = x1;
e[tot].r = e[tot+1].r = x2;
e[tot].h = y1;
e[tot+1].h = y2;
e[tot].v = 1 , e[tot+1].v = -1;
tot += 2;
}
sort(e,e+tot,cmp);
int ans=0; //周长
int last=0; //保存上一次的总区间的被覆盖的长度
build(mn,mx,1);
//每两条横线之间才会有竖线
for(int i=0;i<tot;i++)
{
update(e[i].l,e[i].r-1,1,e[i].v);
//横线:现在这次总区间被覆盖的程度和上一次总区间被覆盖的长度之差的绝对值
ans += abs(p[1].len-last);
//竖线:[下一条横线的高度-现在这条横线的高度]*2*num
ans += (e[i+1].h-e[i].h)*2*p[1].num;
last = p[1].len; //每次都要更新上一次总区间覆盖的长度
}
printf("%d\n",ans);
return 0;
}