zoukankan      html  css  js  c++  java
  • 【bzoj2300】[HAOI2011]防线修建 离线+STL-set维护凸包

    题目描述

    给你(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(nlog 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
    " , ans[i]);
    	return 0;
    }
    

     

  • 相关阅读:
    Hihocoder-小Hi的烦恼
    Python包下载与离线安装
    Shell输出颜色设置
    MySQL主从配置
    MySQL初始化与用户配置
    [转]常用 GDB 命令中文速览
    搭建github静态博客
    树莓派上手
    vim安装与配置
    数组,看了你就懂了!
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/8075254.html
Copyright © 2011-2022 走看看