1. 程式人生 > 其它 >掃描線+線段樹詳解

掃描線+線段樹詳解

前言

這是我的第2篇cnblog部落格,詳細地解說了掃描線+線段樹的工作原理以及程式碼。

前置知識

  • 離散化
  • 線段樹

沒有學過前置知識的同學可以先學習一下在回來學習這篇部落格。

Atlantis問題

我們先來看一道掃描線最經典的例題:Atlantis(或者這個
題意:給你\(n\)個矩形(\(n\le 100\)),它們有可能重疊在一起,求它們一共覆蓋的面積。
掃描線,顧名思義就是一條線在圖上掃描。
話不多說,先看圖(可能有億點醜):

這個圖中,紫色的線條就是掃描線,將掃描線從下往上掃描每一次計算兩個掃描線之間的面積(很好計算,因為是矩形)。
設兩條掃描線是\(y=y_1\)\(y=y_2\)

,每個矩形的寬我們都知道是\(y_2-y_1\),我們現在即要求矩形的長。
而這時候,我們利用類似差分的思想,將每一個矩形的下面打上\(1\)的標籤,上面打上\(-1\)的標籤,用線段樹維護區間和即可。
下面是程式碼(碼風有點醜):

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn=220;
struct Line
{
	double x1,x2,y;
	int flag;
};
struct Node
{
	int l,r,flag;
	double sum;
}tree[maxn<<2];
vector<Line> line;
vector<double> vx;
Line makeline(double x1,double x2,double y,int flag)
{
	Line ret;
	ret.x1=x1;
	ret.x2=x2;
	ret.y=y;
	ret.flag=flag;
	return ret;
}
bool cmp(Line a,Line b)
{
	return a.y<b.y;
}
int get(double x)
{
	return lower_bound(vx.begin(),vx.end(),x)-vx.begin()+1;
}
void pushup(int x)
{
	if(tree[x].flag>0)
	{
		tree[x].sum=vx[tree[x].r]-vx[tree[x].l-1];
	}
	else
	{
		tree[x].sum=tree[x*2].sum+tree[x*2+1].sum;
	}
}
void build(int x,int l,int r)
{
	if(l>r) return;
	tree[x].l=l,tree[x].r=r;
	tree[x].flag=tree[x].sum=0;
	if(l==r) return;
	int mid=(l+r)/2;
	build(x*2,l,mid);
	build(x*2+1,mid+1,r);
}
void update(int x,int l,int r,int delta)
{
	if(l>r) return;
	if(tree[x].l>=l&&tree[x].r<=r)
	{
		tree[x].flag+=delta;
		pushup(x);
		return;
	}
	int mid=(tree[x].l+tree[x].r)/2;
	if(l<=mid)
	{
		update(x*2,l,r,delta);
	}
	if(r>mid)
	{
		update(x*2+1,l,r,delta);
	}
	pushup(x);
}
int n;
int main()
{
	int t=0;
	while(scanf("%d",&n))
	{
		if(!n) break;
		++t;
		line.clear();
		vx.clear();
		for(int i=1;i<=n;i++)
		{
			double x1,x2,y1,y2;
			scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
			line.push_back(makeline(x1,x2,y1,1));
			line.push_back(makeline(x1,x2,y2,-1));
			vx.push_back(x1);
			vx.push_back(x2);
		}
		sort(vx.begin(),vx.end());
		vx.resize(unique(vx.begin(),vx.end())-vx.begin());
		sort(line.begin(),line.end(),cmp);
		build(1,1,(int)vx.size()-1);
		update(1,get(line[0].x1),get(line[0].x2)-1,line[0].flag);
		double ans=0;
		for(int i=1;i<(int)line.size();i++)
		{
			ans+=(double)tree[1].sum*(line[i].y-line[i-1].y);
			update(1,get(line[i].x1),get(line[i].x2)-1,line[i].flag);
		}
		printf("Test case #%d\nTotal explored area: %0.2f\n\n",t,ans);
	}
	return 0;
}

本文來自部落格園,作者:jpy_cpp,轉載請註明原文連結:https://www.cnblogs.com/jpy-cpp/p/15117906.html