1. 程式人生 > >【bzoj2300】[HAOI2011]防線修建 離線+STL-set維護凸包

【bzoj2300】[HAOI2011]防線修建 離線+STL-set維護凸包

print main lin alc bzoj log 插入 line ==

題目描述

給你(0,0)、(n,0)、(x,y)和另外m個點,除(0,0)(n,0)外每個點橫坐標都大於0小於n,縱坐標都大於0。

輸入

第一行,三個整數n,x,y分別表示河邊城市和首都是(0,0),(n,0),(x,y)。 第二行,一個整數m。 接下來m行,每行兩個整數a,b表示A國的一個非首都非河邊城市的坐標為(a,b)。 再接下來一個整數q,表示修改和詢問總數。 接下來q行每行要麽形如1 i,要麽形如2,分別表示撤銷第i個城市的保護和詢問。

輸出

對於每個詢問輸出1行,一個實數v,表示修建防線的花費,保留兩位小數

樣例輸入

4 2 1
2

1 2
3 2
5
2
1 1
2
1 2
2

樣例輸出

6.47
5.84
4.47


題解

離線+STL-set維護凸包

很容易想到離線,然後轉變為加點,維護凸殼周長——經典的動態凸包問題。

把所有凸包上的點按橫坐標維護平衡樹,插入一個點時,首先看它是否在凸包內。具體方法:找出其前驅後繼的點,判斷是否上凸。容易驗證這樣時正確的。

然後考慮加入這個點,需要彈掉什麽樣的點:左邊:找該點的前驅以及前驅的前驅,判斷是否上凸,不上凸則彈掉前驅,否則停止。右邊同理。

由於一個點只被刪除一次,因此時間復雜度時 $O(n\log n)$ 的。

判斷上凸可以使用叉積來判斷。

由於本題不需要在凸包上二分,因此平衡樹只需要維護點坐標,使用STL-set即可。

具體還是看代碼吧。

#include <set>
#include <cmath>
#include <cstdio>
#define N 100010
using namespace std;
struct data
{
	int x , y;
	data() {}
	data(int a , int b) {x = a , y = b;}
	bool operator<(const data &a)const {return x == a.x ? y < a.y : x < a.x;}
	data operator-(const data &a)const {return data(x - a.x , y - a.y);}
	int operator*(const data &a)const {return x * a.y - y * a.x;}
	inline double calc() {return sqrt(x * x + y * y);}
}a[N];
set<data> s;
int del[N] , opt[N << 1] , v[N << 1];
double now , ans[N << 1];
inline void modify(data p)
{
	data a , b;
	set<data>::iterator it = s.lower_bound(p);
	b = *it , a = *--it;
	if((p - a) * (b - p) >= 0) return;
	now -= (a - b).calc();
	while(it != s.begin())
	{
		a = *it , b = *--it;
		if((p - a) * (b - a) >= 0) now -= (a - b).calc() , s.erase(a);
		else break;
	}
	it = s.lower_bound(p);
	while(it != --s.end())
	{
		a = *it , b = *++it;
		if((p - a) * (b - a) <= 0) now -= (a - b).calc() , s.erase(a);
		else break;
	}
	it = s.lower_bound(p) , b = *it , a = *--it;
	now += (p - a).calc() + (p - b).calc() , s.insert(p);
}
int main()
{
	int k , x , y , n , m , i;
	scanf("%d%d%d%d" , &k , &x , &y , &n);
	s.insert(data(0 , 0)) , s.insert(data(k , 0)) , s.insert(data(x , y)) , now = data(x , y).calc() + data(x - k , y).calc();
	for(i = 1 ; i <= n ; i ++ ) scanf("%d%d" , &a[i].x , &a[i].y);
	scanf("%d" , &m);
	for(i = 1 ; i <= m ; i ++ )
	{
		scanf("%d" , &opt[i]);
		if(opt[i] == 1) scanf("%d" , &v[i]) , del[v[i]] = 1;
	}
	for(i = 1 ; i <= n ; i ++ )
		if(!del[i])
			modify(a[i]);
	for(i = m ; i ; i -- )
	{
		if(opt[i] == 1) modify(a[v[i]]);
		else ans[i] = now;
	}
	for(i = 1 ; i <= m ; i ++ )
		if(opt[i] == 2)
			printf("%.2lf\n" , ans[i]);
	return 0;
}

【bzoj2300】[HAOI2011]防線修建 離線+STL-set維護凸包