线段树+扫描线
在求重叠矩形总周长或总面积时,可以用扫描线。
一般要开一个结构体 L I N E LINE LINE,记两端的线段。
struct LINE{
int x,ya,yb;
bool In;//加入或删除
friend bool operator <(LINE a,LINE b){return a.x<b.x;}
}line[N];
模版题
求周长的题目,其实可以横竖分别扫一次,但那样太俗了→_→。
struct LINE{
int x,ya,yb;
bool In;
friend bool operator <(LINE a,LINE b){return a.x<b.x;}
}line[N];
int n,cnt;
int prex,preclen,precnt;
ll ans;
struct Segment_Tree{
#define ls (p<<1)
#define rs (p<<1|1)
int n;
struct node{
int l,r,len;//线段端点,长度
bool L,R;//左右端点是否覆盖
int c,clen,cnt;//被一次性整体覆盖的总层数,总覆盖长度,覆盖区间个数
void upd_node(node a,node b){
L=a.L,R=b.R;
clen=a.clen+b.clen;
cnt=a.cnt+b.cnt-(a.R&&b.L);
}
}tr[N<<2];
void init(int _n){
return (void)(n=_n,build(1,n,1));
}//初始化
void push_up(int p){
if(tr[p].c){
tr[p].clen=tr[p].len;
tr[p].L=tr[p].R=tr[p].cnt=1;
}else tr[p].upd_node(tr[ls],tr[rs]);
}//向上传递一层
void build(int l,int r,int p){
tr[p]={l,r,r-l+1,0,0,0,0,0};
if(l==r)return;
int mid=l+r>>1;
build(l,mid,ls);
build(mid+1,r,rs);
push_up(p);
}//建树
#define mid (tr[p].l+tr[p].r>>1)
void Cover(int p,int l,int r){
if(l<=tr[p].l&&tr[p].r<=r){
if(!tr[p].c)tr[p].L=tr[p].R=1,tr[p].cnt=1,tr[p].clen=tr[p].len;
return ++tr[p].c,void();
}
if(mid>=l)Cover(ls,l,r);
if(mid<r)Cover(rs,l,r);
push_up(p);
}
void Uncover(int p,int l,int r){
if(l<=tr[p].l&&tr[p].r<=r){
--tr[p].c;
if(!tr[p].c){
if(tr[p].len==1)tr[p].L=tr[p].R=tr[p].cnt=tr[p].clen=0;
else tr[p].upd_node(tr[ls],tr[rs]);
}
return;
}
if(mid>=l)Uncover(ls,l,r);
if(mid<r)Uncover(rs,l,r);
push_up(p);
}
#undef mid
void upd(LINE a){
if(a.In)Cover(1,a.ya,a.yb-1);
else Uncover(1,a.ya,a.yb-1);
ans+=2*precnt*(a.x-prex)+abs(tr[1].clen-preclen);
precnt=tr[1].cnt;
prex=a.x;
preclen=tr[1].clen;
}
}seg;//维护覆盖区间总长,不连通覆盖区间个数
signed main(){
scanf("%d",&n);
FOR(i,1,n){
int xa,xb,ya,yb;
scanf("%d%d%d%d",&xa,&ya,&xb,&yb);
line[++cnt]={xa,ya+10001,yb+10001,1};
line[++cnt]={xb,ya+10001,yb+10001,0};
}
sort(line+1,line+cnt+1);
prex=line[1].x;
seg.init(20001);
FOR(i,1,cnt)seg.upd(line[i]);
printf("%lld\n",ans);
return 0;
}
求总重叠面积。
const int N=2e2+10;
struct LINE{
db x;
int ya,yb,In;
friend bool operator <(LINE a,LINE b){return a.x<b.x;}
}line[N];
int cas;
int n,m;
db ans,prex;
db xa[N],ya[N],xb[N],yb[N],b[N];
int nya[N],nyb[N],cnt;
struct Segment_Tree{
#define ls (p<<1)
#define rs (p<<1|1)
int n;
struct node{
int l,r,mark,c;
db len,len2;//区间长度,被一次性整体覆盖次数,覆盖两次总长度
}tr[N<<2];
void init(int _n){
n=_n;build(1,n,1);
}//初始化
void push_down(int p){
tr[ls].c+=tr[p].mark,tr[ls].mark+=tr[p].mark;
tr[rs].c+=tr[p].mark,tr[rs].mark+=tr[p].mark;
tr[p].mark=0;
}
void push_up(int p){
if(tr[p].c)tr[p].len2=tr[p].len;
else {
if(tr[p].l==tr[p].r)tr[p].len2=0;
else tr[p].len2=tr[ls].len2+tr[rs].len2;
}
}
void build(int l,int r,int p){
tr[p]={l,r,0,0,b[r+1]-b[l],0};
if(l==r)return;
int mid=l+r>>1;
build(l,mid,ls);
build(mid+1,r,rs);
}//建树
#define mid (tr[p].l+tr[p].r>>1)
void Cover(int p,int l,int r,int d){
if(l<=tr[p].l&&tr[p].r<=r){
tr[p].c+=d,tr[p].mark+=d;
push_up(p);
return;
}push_down(p);
if(mid>=l)Cover(ls,l,r,d);
if(mid<r)Cover(rs,l,r,d);
push_up(p);
}//修改
#undef mid
void query(int p){
if(tr[p].l==tr[p].r)return push_up(p),void();
push_down(p);
query(ls),query(rs);
return push_up(p),void();
}
void upd(LINE a){
query(1);
ans+=(a.x-prex)*tr[1].len2;
Cover(1,a.ya,a.yb-1,a.In);
prex=a.x;
}
}seg;
void C_main(){
cnt=0,m=0,ans=0;
FOR(i,1,n){
cin>>xa[i]>>ya[i]>>xb[i]>>yb[i];
b[++cnt]=ya[i],b[++cnt]=yb[i];
}
sort(b+1,b+cnt+1),cnt=unique(b+1,b+cnt+1)-b-1;
FOR(i,1,n)nya[i]=lower_bound(b+1,b+cnt+1,ya[i])-b,nyb[i]=lower_bound(b+1,b+cnt+1,yb[i])-b;
FOR(i,1,n)line[++m]={xa[i],nya[i],nyb[i],1},line[++m]={xb[i],nya[i],nyb[i],-1};
sort(line+1,line+m+1);
seg.init(cnt-1);
prex=line[1].x;
FOR(i,1,m)seg.upd(line[i]);
cout<<"Test case #"<<++cas<<endl;
cout<<"Total explored area: ";
cout<<fixed<<setprecision(2)<<ans<<endl;
}
signed main(){
while(cin>>n,n)C_main();
return 0;
}
上一题的升级版(稍微改改就过了)。
相关资料
- 线段树详解 (原理,实现与应用) - AC_King - 博客园 (cnblogs.com).
- 线段树详解 (原理,实现与应用)-CSDN博客.
- 线段树,扫描线详解(非常详细)-CSDN博客.
- 一篇文章搞懂扫描线算法(线段树) HDU1542_线段树扫描线算法-CSDN博客.