这人讲的很清楚了。我就再说一些细节。做了离散化之后时间和空间确实很优秀,但其实不做离散化也可以过。 扫描线这个算法,就是从左到右拿一根平行于y轴的线去扫描,遇到线段后进行处理,入边和出边。统计答案要注意。线段树负责维护的其实是一个线段覆盖的问题,我的每个节点表示的是一条线段,即a[l]a[r+1],所以我建树只建到m-1即可。复杂度应该是 O(nlogn)
#include
#include
#include
#include
#define N 5005
using namespace std;
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x*f;
}
int n,m=0,num=0,ans=0,a[N<<1];
struct lines{
int x,y1,y2;
bool f;//true--add;false--delete
}line[N<<1];
struct node{
bool fl,fr;//左右端点是否覆盖
int cover;//完全覆盖次数
int lines;//线段个数
int clength;//覆盖长度
}tree[N<<2];
void build(int p,int l,int r){
tree[p].fl=tree[p].fr=tree[p].cover=tree[p].clength=tree[p].lines=0;
if(l==r) return;//叶子节点l表示线段[a[l],a[l+1]]
int mid=l+r>>1;
build(p<<1,l,mid);build(p<<1|1,mid+1,r);
}
inline bool cmp(lines A,lines B){
if(A.x==B.x) return A.f>B.f;//如果重叠,先入边
return A.xinline void update(int p){
if(tree[p].cover) return;
tree[p].clength=tree[p<<1].clength+tree[p<<1|1].clength;
tree[p].fl=tree[p<<1].fl;tree[p].fr=tree[p<<1|1].fr;
tree[p].lines=tree[p<<1].lines+tree[p<<1|1].lines;
if(tree[p<<1].fr&&tree[p<<1|1].fl) tree[p].lines--;//左右端点连在一起,少一条
}
void cover(int p,int l,int r,int x,int y){
if(x<=l&&r<=y){
if(!tree[p].cover){//如果没完全覆盖
tree[p].fl=tree[p].fr=true;
tree[p].lines=1;tree[p].clength=a[r+1]-a[l];
}
tree[p].cover++;return;
}
int mid=l+r>>1;
if(x<=mid) cover(p<<1,l,mid,x,y);
if(y>mid) cover(p<<1|1,mid+1,r,x,y);
update(p);
}
void uncover(int p,int l,int r,int x,int y){
if(x<=l&&r<=y){
tree[p].cover--;
if(!tree[p].cover){//减没了,要更新
if(l==r){//叶子节点,直接更新
tree[p].fl=tree[p].fr=false;
tree[p].clength=tree[p].lines=0;
}
else update(p);
}
return;
}
int mid=l+r>>1;
if(x<=mid) uncover(p<<1,l,mid,x,y);
if(y>mid) uncover(p<<1|1,mid+1,r,x,y);
update(p);
}
int main(){
// freopen("a.in","r",stdin);
n=read();
while(n--){
int x1=read(),y1=read(),x2=read(),y2=read();
a[++m]=y1;a[++m]=y2;
line[++num].x=x1;line[num].y1=y1;
line[num].y2=y2;line[num].f=true;
line[++num].x=x2;line[num].y1=y1;
line[num].y2=y2;line[num].f=false;
}
sort(a+1,a+m+1);
m=unique(a+1,a+m+1)-a-1;//离散化y值
build(1,1,m-1);
sort(line+1,line+num+1,cmp);
line[0]=line[1];//避免第一次多加
int prelines=0;//上一次线段数
int prelength=0;//上次长度
for(int i=1;i<=num;++i){
if(line[i].f) cover(1,1,m-1,lower_bound(a+1,a+m+1,line[i].y1)-a,lower_bound(a+1,a+m+1,line[i].y2)-a-1);
else uncover(1,1,m-1,lower_bound(a+1,a+m+1,line[i].y1)-a,lower_bound(a+1,a+m+1,line[i].y2)-a-1);
ans+=prelines*2*(line[i].x-line[i-1].x);//先更新水平的长度
prelines=tree[1].lines;
//更新竖直的高度
ans+=abs(tree[1].clength-prelength);
prelength=tree[1].clength;
}
printf("%d",ans);
return 0;
}