Acwing247線段樹+掃描線

題目鏈接:https://www.acwing.com/problem/content/249/

解題思路:
這道題目的意思是想讓我們去計算由許多矩形組成的多邊形的總面積,並且給出的座標可是浮點型的;對於這樣有對稱的線段出現並需要所框住區域中的某種屬性的量,其實都是可以使用掃描線(但是還是得說應用的領域不大);但是在使用掃描線去計算其的面積的時候,需要去統計一個邊的長度,這時候用線段樹就會顯得非常的巧,因爲每對邊都是成對出現的,所以省去了pushdown的操作,只需要一個pushup操作就可以完成了。
所以這道題目的思路是:首先對其座標進行離散化,然後取出每個矩形的兩天邊放到vector容器中進行離散化;然後,將兩條邊分別賦予一個屬性c來表示這條邊是需要加進去還是拿出來。最後對其x進行排序,然後不斷地去查詢線段樹中第一個節點的len即可計算面積。
但是在寫代碼的過程紅也有一些需要注意的地方;首先,線段樹中的每一個點其實存的是這個點所對應的那條邊,即:i 表示 i 到 i + 1.所以有兩個地方需要注意,一個是build的時候,一個是通過find去找l與r的時候,注意都需要多減去一個1.

貼上(蒟蒻)代碼一份

#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>

using namespace std;

const int maxn = 200005; 

int n;
struct Triangle {
	double x, y1, y2;
	int c;
	bool operator< (const Triangle& t)const {
		return x < t.x;
	}
}triangle[maxn];
struct Treement {
	 int l, r;
	 int cnt;
	 double len;
}tr[4 * maxn];
vector<double> ve;

inline int find(double x) {
	return lower_bound(ve.begin(), ve.end(), x) - ve.begin();
}

inline void pushup(int u) {
	if(tr[u].cnt) tr[u].len = ve[tr[u].r + 1] - ve[tr[u].l];
	else if(tr[u].l != tr[u].r) {
		tr[u].len = tr[u << 1].len + tr[u << 1 | 1].len;
	} else tr[u].len = 0;
} 

inline void build(int u, int l, int r) {
	tr[u] = {l, r, 0, 0};
	if(l != r) {
		int mid = l + r >> 1;
		build(u << 1, l, mid); build(u << 1 | 1, mid + 1, r); 
	}
}

inline void modify(int u, int l, int r, int c) {
	if(l <= tr[u].l && tr[u].r <= r) {
		tr[u].cnt += c;
		pushup(u);
		return;
	}

	int mid = tr[u].l + tr[u].r >> 1;
	if(l <= mid) modify(u << 1, l, r, c);
	if(r > mid) modify(u << 1 | 1, l, r, c);

	pushup(u);
}

int main(void) {
//	freopen("in.txt", "r", stdin);
	int count = 0;
	while(scanf("%d", &n) != EOF && n) {
		ve.clear();
		
		for(int i = 1, j = 0; i <= n; i ++) {
			double x1, y1, x2, y2;
			scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2);
			triangle[j ++] = {x1, y1, y2, 1};
			triangle[j ++] = {x2, y1, y2, -1};
			ve.push_back(y1), ve.push_back(y2);
		}
//		puts("33");
		
		sort(ve.begin(), ve.end());
		ve.erase(unique(ve.begin(), ve.end()), ve.end());		
		
		build(1, 0, ve.size() - 2);  //由於在這個線段樹中,每個節點所代表的是一個線段,而不是一個點,
		                             //所以這裏需要在原來的基礎上再 減去一個1;即:i:i - i + 1 
		sort(triangle, triangle + 2 * n);
	
		double fi = 0;
		double x1 = 0, x2 = 0;
		for(int i = 0; i < 2 * n; i ++) {
			//先計算面積 
			if(i > 0) fi += tr[1].len * (triangle[i].x - triangle[i - 1].x);
		
			//再將這段線段放到線段樹上去 
			int l, r;
			l = find(triangle[i].y1);
			r = find(triangle[i].y2) - 1; 
			modify(1, l, r, triangle[i].c); 
		}
		
		printf("Test case #%d\nTotal explored area: %.2lf\n\n", ++ count, fi);
	}

	return 0;
} 

總結:要注意細節,尤其是對線段樹每個節點所代表的的含義要牢記。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章