1.动态凸包是一类经典的题目,题目的大概意思就是:在添加和删除平面上的一些点,然后询问这些点构成的凸包的信息。典型题目:HAOI 2011 构建。有多种写法,可以用Splay维护,也可以用set维护。我们来讨论一下如何使用set维护动态凸包,但是有一个缺陷,就是只支持添加点,删除点的是不行的,但是我们可以把操作离线下来,倒着扫,删点就成了加点。
(1)首先我们把所有的点按照x排序,x相同的按照y排序。假设我们已经得到了某个凸包:
现在加入x点,
显然,x点是要凸包上的一个点,我们首先在set内lowerbound一个点,r,然后r像后边退一个得到l。因为我们是看出来这个点的确实需要被加入凸包集合内,但是如何判断呢?
做rx向量,lx向量,我们可以看出来,lx*rx(叉乘)是一个正的数字,直白点就是说rx转向rx的角度是一个小于180的角,那么这个点就要被加入凸包内,否则不加入。加入以后,我们如何构建新的凸包呢?方法类似,因为我们已经得到了l,r.以l为例:
将l向左推移,得到l1,发现l1x转向lx的夹角的sin值也是一个正值,所以l1继续留在凸包内部,否则从集合删除这个点。这样把l一直左推到起点就完成了左半部分凸包的更新,右半部分类似。这样每次更新的复杂度是O(size(set))的。所以这种方法虽然比Splay好实现,但是极限数据时可能会不是很好。
简单的贴一下代码:
/*
凸包被存在st中,st是一个set。point结构体重载了*,重载为叉积。it为宏定义,为set的迭代器
ans:凸包的周长,calc()为计算两点间距离的函数。动态维护凸包的周长
*/
inline void insert(point x)
{
it r = st.lower_bound(x), l = r, t;
l--;
if ((*r - *l) * (x - *l) < 0)return;
ans -= (*r - *l).calc();
while (1)
{
t = r, r++;
if (r == st.end())break;
if ((*r - x) * (*t - x) > 0)break;
ans -= (*r - *t).calc();
st.erase(t);
}
while (l != st.begin())
{
t = l, l--;
if ((*l - x) * (*t - x) < 0)break;
ans -= (*l - *t).calc();
st.erase(t);
}
st.insert(x);
l = r = st.find(x);
l--, r++;
ans += (*r - x).calc(), ans += (*l - x).calc();
}