[BZOJ2300][HAOI2011][动态凸包]防线修建

[Problem Description]
近来 A国和 B国的矛盾激化,为了预防不测, A国准备修建一条长长的防线,当然修建防线的话,肯定要把需要保护的城市修在防线内部了。可是 A国上层现在还犹豫不决,到底该把哪些城市作为保护对象呢?又由于 A国的经费有限,所以希望你能帮忙完成如下的一个任务:
1. 给出你所有的 A 国城市坐标
2. A 国上层经过讨论,考虑到经济问题,决定取消对 i 城市的保护,也就是说 i 城市不需要在防线内了
3. A 国上层询问对于剩下要保护的城市,修建防线的总经费最少是多少.你需要对每次询问作出回答。注意单位 1长度的防线花费为 1
A国的地形是这样的,形如下图, x轴是一条河流,相当于一条天然防线,不需要你再修建
A国总是有两个城市在河边,一个点是 (0,0),一个点是 (n,0),其余所有点的横坐标均大于 0小于 n,纵坐标均大于 0A国有一个不在 (0,0)(n,0)的首都。 (0,0),(n,0)和首都这三个城市是一定需要保护的。

上图中, A,B,C,D,E点为 A国城市,且目前都要保护,那么修建的防线就会是 A-B-C-D,花费也就是线段 AB的长度 +线段 BC的长度 +线段 CD的长度。
如果,这个时候撤销 B 点的保护,那么防线变成下图

[Algorithm & Data Structure]
动态凸包
[Analysis]
明显的动态凸包(其实暴力凸包也可以过)。删点比较不好搞,所以倒着算,先把所有删掉的点去掉,然后一个个往里面加。动态凸包写Splay比较蛋疼……
[Code]
/**************************************************************
    Problem: 2300
    User: gaotianyu1350
    Language: C++
    Result: Accepted
    Time:620 ms
    Memory:7640 kb
****************************************************************/
 
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <iostream>
using namespace std;
 
const int MAXN = 100100;
const int MAXQ = 200100;
 
class splay_node
{
public:
    splay_node *ch[2], *pre;
    int x, y, cnt;
    splay_node();
 
    bool operator < (const splay_node b) const
    {
        return (x == b.x && y < b.y) || x < b.x;
    }
 
    bool operator == (const splay_node b) const
    {
        return (x == b.x && y == b.y);
    }
 
    int Which()
    {
        return pre->ch[0] == this ? 0 : 1;
    }
 
    void Set(splay_node *child, int wh)
    {
        ch[wh] = child, child->pre = this;
    }
 
} splay[MAXN];
 
int tot = 0;
splay_node *root;
 
splay_node::splay_node()
{
    ch[0] = ch[1] = pre = splay;
    x = 0;
    y = 0;
    cnt = 1;
}
 
inline void Rotate(splay_node *now)
{
    splay_node *oldfather = now->pre;
    splay_node *grand = now->pre->pre;
    int wh = now->Which();
    oldfather->Set(now->ch[wh ^ 1], wh);
    now->Set(oldfather, wh ^ 1);
    now->pre = grand;
    if (grand != splay)
        grand->ch[grand->ch[0] == oldfather ? 0 : 1] = now;
}
 
inline void Splay(splay_node *now, splay_node *tar)
{
    for (; now->pre != tar; Rotate(now))
        if (now->pre->pre != tar)
            now->Which() == now->pre->Which() ? Rotate(now->pre) : Rotate(now);
    if (tar == splay)
        root = now;
}
 
inline splay_node *Find(int x, int y)
{
    splay_node tar;
    tar.x = x, tar.y = y;
    splay_node *now = root;
    while (now != splay)
    {
        if (*now == tar) return now;
        if (tar < *now)
            now = now->ch[0];
        else
            now = now->ch[1];
    }
    return now;
}
 
inline splay_node *GetPre(splay_node *now)
{
    Splay(now, splay);
    for (now = now->ch[0]; now->ch[1] != splay; now = now->ch[1]);
    return now;
}
 
inline splay_node *GetNext(splay_node *now)
{
    Splay(now, splay);
    for (now = now->ch[1]; now->ch[0] != splay; now = now->ch[0]);
    return now;
}
 
inline splay_node *NewNode(int x, int y)
{
    tot++;
    splay[tot].x = x, splay[tot].y = y;
    return splay + tot;
}
 
inline splay_node *Add(int x, int y)
{
    splay_node tar;
    tar.x = x, tar.y = y;
    splay_node *now = root;
    splay_node *last = splay;
 
    if (root == splay)
    {
        root = NewNode(x, y);
        return root;
    }
 
    while (now != splay)
    {
        last = now;
        if (*now == tar)
        {
            now->cnt++;
            return now;
        }
        if (tar < *now)
            now = now->ch[0];
        else
            now = now->ch[1];
    }
 
    now = NewNode(x, y);
    if (tar < *last)
        last->Set(now, 0);
    else
        last->Set(now, 1);
    Splay(now, splay);
    return now;
}
 
inline void Delete(splay_node *now = splay, int x = 0, int y = 0)
{
    if (now == splay)
        now = Find(x, y);
    if (now->cnt > 1)
    {
        now->cnt--;
        return;
    }
    Splay(now, splay);
    if (now->ch[0] == splay && now->ch[1] == splay)
    {
        root = splay;
        return;
    }
    if (now->ch[0] == splay || now->ch[1] == splay)
    {
        root = now->ch[0] == splay ? now->ch[1] : now->ch[0];
        root->pre = splay;
        return;
    }
    splay_node *_pre = GetPre(root);
    Splay(_pre, root);
    _pre->Set(root->ch[1], 1);
    root = _pre;
    root->pre = splay;
}
 
int n, x, y, m;
int query;
int xx[MAXN], yy[MAXN];
bool check[MAXN] = { 0 };
 
struct QueryData
{
    int k, x;
    double ans;
} qd[MAXQ];
 
double nowans;
 
inline double dis(int x1, int y1, int x2, int y2)
{
    return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}
 
inline double dis(splay_node *a, splay_node *b)
{
    return dis(a->x, a->y, b->x, b->y);
}
 
inline int cross(splay_node *a, splay_node *b, splay_node *c)
{
    return (b->x - a->x) * (c->y - b->y) - (c->x - b->x) * (b->y - a->y);
}
 
inline bool isstart(splay_node *now)
{
    return (now->x == 0 && now->y == 0);
}
 
inline bool isend(splay_node *now)
{
    return (now->x == n && now->y == 0);
}
 
inline void AddACity(int x, int y)
{
    splay_node *now = Add(x, y);
    splay_node *_pre = GetPre(now);
    splay_node *_next = GetNext(now);
    if (cross(_pre, now, _next) >= 0)
    {
        Delete(now);
        return;
    }
    nowans -= dis(_pre, _next);
    if (!isstart(_pre))
        for (splay_node *_prepre = GetPre(_pre); cross(_prepre, _pre, now) >= 0;)
        {
             nowans -= dis(_pre, _prepre);
             Delete(_pre);
             _pre = _prepre;
             if (isstart(_pre)) break;
             _prepre = GetPre(_pre);
        }
    if (!isend(_next))
        for (splay_node *_nextnext = GetNext(_next); cross(now, _next, _nextnext) >= 0;)
        {
             nowans -= dis(_next, _nextnext);
             Delete(_next);
             _next = _nextnext;
             if (isend(_next)) break;
             _nextnext = GetNext(_next);
        }
    nowans += dis(now, _pre) + dis(now, _next);
}
 
int main()
{
    //freopen("input.txt", "r", stdin);
    scanf("%d%d%d", &n, &x, &y);
    root = splay;
    Add(0, 0);
    Add(n, 0);
    Add(x, y);
    nowans = dis(0, 0, x, y) + dis(x, y, n, 0);
    scanf("%d", &m);
    for (int i = 1; i <= m; i++)
        scanf("%d%d", &xx[i], &yy[i]);
    scanf("%d", &query);
    for (int i = 1; i <= query; i++)
    {
        scanf("%d", &qd[i].k);
        if (qd[i].k == 1)
        {
            scanf("%d", &qd[i].x);
            check[qd[i].x] = true;
        }
    }
    for (int i = 1; i <= m; i++)
        if (!check[i]) AddACity(xx[i], yy[i]);
    for (int i = query; i >= 1; i--)
        if (qd[i].k == 1)
            AddACity(xx[qd[i].x], yy[qd[i].x]);
        else
            qd[i].ans = nowans;
    for (int i = 1; i <= query; i++)
        if (qd[i].k == 2)
            printf("%.2f\n", qd[i].ans);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值