分析:
从左往右在每一次插入一条边后,周长并的累加值=新增的竖边+新增的横边
原题重做
代码重构了一下,这样就和求矩阵面积并的代码统一起来了(好记)
随意看一下代码:
我们是在y坐标上建立线段树:
在处理矩形信息的时候,我们拆成两个操作
对于x坐标相同的操作,我们先插入再删除(处理重边)
int cmp(const point &a,const point &b) { //先插入后删除
return (a.x<b.x)||(a.x==b.x&&a.type>b.type);
}
int xa,ya,xb,yb;
for (int i=1;i<=n;i++) {
scanf("%d%d%d%d",&xa,&ya,&xb,&yb);
tot++; Y[tot]=ya;
a[tot].x=xa; a[tot].yl=ya; a[tot].yr=yb; a[tot].type=1;
tot++; Y[tot]=yb;
a[tot].x=xb; a[tot].yl=ya; a[tot].yr=yb; a[tot].type=-1;
}
struct node{
int l,r,ml,mr,len;
int cover,lp,rp,cnt;
}t[N<<2];
线段树中维护:
l,r:
l
,
r
:
线段树中的左右端点
ml,mr:
m
l
,
m
r
:
该结点维护的真实区间长度
len:
l
e
n
:
该区间中被覆盖的线段长度
cover:
c
o
v
e
r
:
覆盖标记
lp,rp:
l
p
,
r
p
:
左右端点是否被覆盖
cnt:
c
n
t
:
该区间会产生多少水平线
在统计答案时候,对于竖线,我们只统计增量abs(delta-t[1].len)
ll ans=0;
int delta=0;
for (int i=1;i<=tot;i++) {
add(1,a[i]);
ans+=(ll)abs(delta-t[1].len);
delta=t[1].len;
if (i!=tot) ans+=(ll)(a[i+1].x-a[i].x)*t[1].cnt;
}
printf("%lld\n",ans);
因为图形多半是在平面直角坐标系上的,所以扫描线的build函数有一点不同
这种build树方式,会影响到之后的add和update
void build(int bh,int l,int r) {
t[bh].l=l; t[bh].r=r;
t[bh].ml=Y[l]; t[bh].mr=Y[r];
t[bh].lp=t[bh].rp=t[bh].cnt=t[bh].len=0;
if (l+1==r) return;
int mid=(l+r)>>1;
build(bh<<1,l,mid); //mid
build(bh<<1|1,mid,r); //mid
}
插入操作的时候,我们标记永久化(毕竟我们只关心t[1]的信息,update就好了)
注意我们在完全覆盖之后,要update一下
在把区间分给两个儿子的时候,注意范围的判断a.yr<=t[bh<<1].mr,t[bh<<1|1].ml<=a.yl
(有等号)
如果区间跨越两个儿子,我们就用下面的方式分到两个儿子中
(在求矩形面积并时也是这样处理的)
void add(int bh,point a) {
if (t[bh].ml==a.yl&&t[bh].mr==a.yr) {
t[bh].cover+=a.type;
update(bh); //维护一下
return;
}
if (a.yr<=t[bh<<1].mr) add(bh<<1,a);
else if (t[bh<<1|1].ml<=a.yl) add(bh<<1|1,a);
else {
point v=a;
v.yr=t[bh<<1].mr;
add(bh<<1,v);
v=a;
v.yl=t[bh<<1|1].ml;
add(bh<<1|1,v);
}
update(bh);
}
最后就是我们最重要的update
如果完全覆盖,没什么可说的
不过如果没有覆盖标记又是叶子结点,我们要记得把ta的标记都清空
(没有这个处理,好像在多组数据的情况下会发生一些意外,一切以稳为第一要义)
如果当前结点的信息需要从儿子维护上来,注意特判一下cnt
void update(int bh) {
int lc=bh<<1;
int rc=bh<<1|1;
if (t[bh].cover>0) {
t[bh].len=t[bh].mr-t[bh].ml;
t[bh].lp=t[bh].rp=1;
t[bh].cnt=2;
}
else if (t[bh].l+1==t[bh].r){ //important
t[bh].len=0;
t[bh].lp=t[bh].rp=t[bh].cnt=0;
}
else {
t[bh].len=t[lc].len+t[rc].len;
t[bh].lp=t[lc].lp;
t[bh].rp=t[rc].rp;
t[bh].cnt=t[lc].cnt+t[rc].cnt;
if (t[lc].rp&&t[rc].lp) t[bh].cnt-=2;
}
}
tip
多组数据
注意初始化
我们的口号是:稳是第一要义
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int N=10010;
struct node{
int l,r,ml,mr,len;
int cover,lp,rp,cnt;
}t[N<<2];
struct point{
int x,yl,yr,type;
}a[N];
int n,Y[N],tot,nn;
int cmp(const point &a,const point &b) { //先插入后删除
return (a.x<b.x)||(a.x==b.x&&a.type>b.type);
}
void build(int bh,int l,int r) {
t[bh].l=l; t[bh].r=r;
t[bh].ml=Y[l]; t[bh].mr=Y[r];
t[bh].lp=t[bh].rp=t[bh].cnt=t[bh].len=0;
if (l+1==r) return;
int mid=(l+r)>>1;
build(bh<<1,l,mid);
build(bh<<1|1,mid,r);
}
void update(int bh) {
int lc=bh<<1;
int rc=bh<<1|1;
if (t[bh].cover>0) {
t[bh].len=t[bh].mr-t[bh].ml;
t[bh].lp=t[bh].rp=1;
t[bh].cnt=2;
}
else if (t[bh].l+1==t[bh].r){
t[bh].len=0;
t[bh].lp=t[bh].rp=t[bh].cnt=0;
}
else {
t[bh].len=t[lc].len+t[rc].len;
t[bh].lp=t[lc].lp;
t[bh].rp=t[rc].rp;
t[bh].cnt=t[lc].cnt+t[rc].cnt;
if (t[lc].rp&&t[rc].lp) t[bh].cnt-=2;
}
}
void add(int bh,point a) {
if (t[bh].ml==a.yl&&t[bh].mr==a.yr) {
t[bh].cover+=a.type;
update(bh);
return;
}
if (a.yr<=t[bh<<1].mr) add(bh<<1,a);
else if (t[bh<<1|1].ml<=a.yl) add(bh<<1|1,a);
else {
point v=a;
v.yr=t[bh<<1].mr;
add(bh<<1,v);
v=a;
v.yl=t[bh<<1|1].ml;
add(bh<<1|1,v);
}
update(bh);
}
int main()
{
while (scanf("%d",&n)!=EOF) {
memset(Y,0,sizeof(Y));
tot=0; //初始化啊
int xa,ya,xb,yb;
for (int i=1;i<=n;i++) {
scanf("%d%d%d%d",&xa,&ya,&xb,&yb);
tot++; Y[tot]=ya;
a[tot].x=xa; a[tot].yl=ya; a[tot].yr=yb; a[tot].type=1;
tot++; Y[tot]=yb;
a[tot].x=xb; a[tot].yl=ya; a[tot].yr=yb; a[tot].type=-1;
}
sort(Y+1,Y+1+tot);
nn=unique(Y+1,Y+1+tot)-Y-1;
build(1,1,nn);
sort(a+1,a+1+tot,cmp);
ll ans=0;
int delta=0;
for (int i=1;i<=tot;i++) {
add(1,a[i]);
ans+=(ll)abs(delta-t[1].len);
delta=t[1].len;
if (i!=tot) ans+=(ll)(a[i+1].x-a[i].x)*t[1].cnt;
}
printf("%lld\n",ans);
}
return 0;
}