洛谷P2521 [HAOI2011]防线修建
? 解题记录 ? ? 洛谷 ? ? 平衡树 ? ? 凸包 ?    2018-04-27 21:51:34    471    0    0

题目描述

近来A国和B国的矛盾激化,为了预防不测,A国准备修建一条长长的防线,当然修建防线的话,肯定要把需要保护的城市修在防线内部了。可是A国上层现在还犹豫不决,到底该把哪些城市作为保护对象呢?又由于A国的经费有限,所以希望你能帮忙完成如下的一个任务:

  1. 给出你所有的A国城市坐标

  2. A国上层经过讨论,考虑到经济问题,决定取消对i城市的保护,也就是说i城市不需要在防线内了

  3. A国上层询问对于剩下要保护的城市,修建防线的总经费最少是多少

你需要对每次询问作出回答。注意单位1长度的防线花费为1。

A国的地形是这样的,形如下图,x轴是一条河流,相当于一条天然防线,不需要你再修建

A国总是有两个城市在河边,一个点是(0,0),一个点是(n,0),其余所有点的横坐标均大于0小于n,纵坐标均大于0。A国有一个不在(0,0)和(n,0)的首都。(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,表示修建防线的花费,保留两位小数

 

输入输出样例

输入样例#1: 复制
4 2 1                                
2                                 
1 2                               
3 2                               
5                                 
2
1 1
2
1 2
2
输出样例#1: 复制
6.47
5.84
4.47

说明

数据范围:

30%的数据m<=1000,q<=1000

100%的数据m<=100000,q<=200000,n>1

所有点的坐标范围均在10000以内, 数据保证没有重点

本题是动态凸包裸题。我们考虑用set维护在凸包上的点,set以横坐标排序。每一次插入一个点的时候先查询是否在凸包内,查询的方法为在set中二分出该点左右的点,判断点是否在这两点连成的直线下。如果不在凸包内,我们就插入该点,每一次插入弹出左右两边不符合凸包性质的点,并且时刻更新周长即可。

 

#include<cstdio>
#include<cmath>
#include<set>
using namespace std;
const int maxn = 1e5 + 5, inf = 0x3f3f3f3f;
typedef pair<int, int> pii;
set<pii > up;
set<pii >::iterator it, lst, ml, mr;
int xMX, cx, cy, n, q, opr[maxn][2], vis[maxn];
double k, b, mx, my, C, mk;
pii pos[maxn];

bool InCon(int x, int y) {
	it = up.lower_bound(make_pair(x, -inf)), mr = it;
	mx = (double) (it -> first), my = (double) (it -> second);
	k = ((double)(--it) -> second - my) / ((double)(it) -> first - mx);
	b = my - (k * mx), mk = k, ml = it;
	return (k * x + b) >= y;
}

#define sqr(a) ((double)(a) * (a))

inline double Gdis(pii a, pii b) {
	return sqrt(sqr(a.first - b.first) + sqr(a.second - b.second));
}

void InsPos(int x, int y) {
	if(InCon(x, y)) return ;
	C -= Gdis(*ml, *mr), it = ml;
	while(1) {
		mx = it -> first, my = it -> second, lst = it--;
		if(lst == up.begin()) break;
		k = ((double)(it) -> second - my) / ((double)(it) -> first - mx);
		if(k <= ((double)y - (double)(lst) -> second) / ((double)x - (double)(lst) -> first))
		C -= Gdis(*lst, *it), up.erase(lst); else break;
	}
	C += Gdis(*lst, make_pair(x, y));
	it = mr, k = mk;
	while(1) {
		mx = it -> first, my = it -> second, lst = it++;
		if(it == up.end()) break;
		k = ((double)(it) -> second - my) / ((double)(it) -> first - mx);
		if(k >= ((double)(lst) -> second - (double)y) / ((double)(lst) -> first - (double)x))
		C -= Gdis(*lst, *it), up.erase(lst); 
		else break;
	}
	C += Gdis(*lst, make_pair(x, y));
	up.insert(make_pair(x, y));
}

void prt(int st) {
	double ans = 0;
	if(st == 0) return;
	if(opr[st][0] == 1) InsPos(pos[opr[st][1]].first, pos[opr[st][1]].second);
	ans = C, prt(st - 1);
	if(opr[st][0] == 2) printf("%.2lf\n", ans);
}

int main() {
	scanf("%d%d%d", &xMX, &cx, &cy);
	up.insert(make_pair(xMX, 0));
	up.insert(make_pair(0, 0));
	up.insert(make_pair(cx, cy));
	C = Gdis(make_pair(xMX, 0), make_pair(cx, cy));
	C += Gdis(make_pair(0, 0), make_pair(cx, cy));
	scanf("%d", &n);
	for(register int i = 1; i <= n; ++i)
		scanf("%d%d", &pos[i].first, &pos[i].second);
	scanf("%d", &q);
	for(register int i = 1; i <= q; ++i) {
		scanf("%d", &opr[i][0]);
		if(opr[i][0] == 2) continue;
		scanf("%d", &opr[i][1]), vis[opr[i][1]] = 1;
	}
	for(register int i = 1; i <= n; ++i)
		if(!vis[i]) InsPos(pos[i].first, pos[i].second);
	prt(q);
	return 0;
}

 

上一篇: 洛谷P3586 [POI2015]LOG

下一篇: HDU4812 D Tree

471 人读过
立即登录, 发表评论.
没有帐号? 立即注册
0 条评论
文档导航