1. 程式人生 > >Gene_I must wait for the sunrise

Gene_I must wait for the sunrise

題意:

      本題是掃描線的典型應用,就是求矩形的面積交。與本題相關的題就是HDU1542,求矩形面積並。

思路:

      我們先來討論一下掃描線的本質。掃描線演算法其實就是先將所有的 y 座標進行離散化,然後用所有離散的點建立一棵線段樹,這棵線段樹的每一個節點的左右端點就代表空間中 y 座標上的一段區間。

      然後我們再將每一個矩形的左右兩條邊抽出來,進行排序,然後一一存進線段樹中。

      每一根線記錄一個flag屬性,左線標記為1,右線標記為-1,然後將每一根線存入線段樹中的時候,就會對線段樹的某一段區間進行覆蓋,然後我們需要記錄每一個區間被覆蓋的次數。然後每一根線段所帶來的面積就是 ans += t[1].len * ( line[i].x - line[i-1].x ),由此便可以求出所有被覆蓋過一次的面積。

      那麼本題就是掃描線模板的一個變種。

      我們依舊記錄每一個線段樹節點被完全覆蓋的次數,而且當線段樹中某一個節點被覆蓋了一次,那麼該節點的兒子不會被標記為覆蓋,所以區間的覆蓋都是獨立的。

      然後我們再記錄一下每個節點被覆蓋一次以上的長度,和兩次以上的長度。

      那麼對於calc()函式,

當這段區間被覆蓋了兩次以上,則該區間的len2 = len1 = t[p].mr-t[p].ml;

當這段區間被覆蓋了一次,則先判斷這是不是葉節點,如果不是葉節點,則len2 = t[p*2].len1+t[p*2+1].len1,此處就是本題的關鍵不同之處,其餘條件語句詳見程式碼。

總結:

      對於掃描線的問題,本質就在於離散化,然後標記每一個區間被覆蓋了多少次,而且每一個區間被覆蓋都是獨立的。然後就是多做掃描線的題了,然後熟練掌握此類問題的解決方法。

程式碼:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#define rep(i,a,b) for(int i = a; i <= b; i++)
using namespace std;
const int N = 4000;

struct Line{
	double x,y1,y2;
	int flag;
}line[N];
int n, num;
double y[N], ans;
struct Tree{
	int l,r,s;
	double ml,mr,len1,len2; //記錄覆蓋一次以上的長度 和 覆蓋兩次以上的長度
}t[N*4];

bool cmp(Line a,Line b)
{
	return a.x < b.x;
}

void build(int p,int l,int r)
{
	t[p].l = l, t[p].r = r, t[p].ml = y[l], t[p].mr = y[r], t[p].s = 0, t[p].len1 = 0, t[p].len2 = 0;
//	cout << t[p].ml << " " << t[p].mr << endl;
	if(l == (r-1)) return;
	int mid = (l+r)>>1;
	build(p*2,l,mid);
	build(p*2+1,mid,r);
}

void calc(int p)
{
	if(t[p].s >= 2){
		t[p].len1 = t[p].len2 = t[p].mr-t[p].ml;
	}
	else if(t[p].s == 1){ //如果這段區間只被完全覆蓋過一次,那麼這段區間被覆蓋兩次的長度為 左右兒子被覆蓋一次的長度之和
		//因為我們寫掃描線這題的時候,如果一個區間被完全覆蓋,那麼這段區間被覆蓋的次數+1
		//但是我們沒有下傳覆蓋標誌,所以這段區間的左右兒子並沒有被標記覆蓋
		//所以每一段區間被覆蓋的次數都是獨立的
		if(t[p].r == (t[p].l+1)) t[p].len2 = 0;
		else t[p].len2 = t[p*2].len1+t[p*2+1].len1;
		t[p].len1 = t[p].mr-t[p].ml;
	}
	else if(t[p].r == (t[p].l+1)){
		t[p].len1 = t[p].len2 = 0;
	}
	else{
		t[p].len1 = t[p*2].len1+t[p*2+1].len1;
		t[p].len2 = t[p*2].len2+t[p*2+1].len2;
	} 
}

void change(int p, Line tp)
{
	if(tp.y1 <= t[p].ml && tp.y2 >= t[p].mr)
	{
		t[p].s += tp.flag;
		calc(p);
		return;
	}
	if(t[p*2].mr >= tp.y2) change(p*2,tp);
	else if(t[p*2+1].ml <= tp.y1) change(p*2+1,tp);
	else{
		change(p*2,tp);
		change(p*2+1,tp);
	} 
	calc(p);
}

int main()
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
		ans = 0, num = 0;
		scanf("%d",&n);
		rep(i,1,n){
			double x1,x2,y1,y2;
			scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
			line[++num].x = x1, line[num].y1 = y1, line[num].y2 = y2, line[num].flag = 1;
			y[num] = y1;
			line[++num].x = x2, line[num].y1 = y1, line[num].y2 = y2, line[num].flag = -1;			
			y[num] = y2;
		}
		sort(line+1,line+1+num,cmp);
		sort(y+1,y+1+num);
		int sc = unique(y+1,y+num+1)-y-1;	
		build(1,1,sc);
		change(1,line[1]);
		rep(i,2,num)
		{
			ans += t[1].len2*(line[i].x-line[i-1].x);
			change(1,line[i]);
		}
		printf("%.2f\n",ans);
	}
	return 0;
}