[BZOJ]2300 [HAOI2011] 防线修建 动态维护凸包

2300: [HAOI2011]防线修建

Time Limit: 10 Sec  Memory Limit: 512 MB
Submit: 1015  Solved: 558
[ Submit][ Status][ Discuss]

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,纵坐标均大于0。A国有一个不在(0,0)和(n,0)的首都。(0,0),(n,0)和首都这三个城市是一定需要保护的。

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


Input

第一行,三个整数n,x,y分别表示河边城市和首都是(0,0),(n,0),(x,y)。
第二行,一个整数m。
接下来m行,每行两个整数a,b表示A国的一个非首都非河边城市的坐标为(a,b)。
再接下来一个整数q,表示修改和询问总数。
接下来q行每行要么形如1 i,要么形如2,分别表示撤销第i个城市的保护和询问。

Output

对于每个询问输出1行,一个实数v,表示修建防线的花费,保留两位小数

Sample Input

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

Sample Output

6.47
5.84
4.47

HINT

m<=100000,q<=200000,n>1

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

Source

[ Submit][ Status][ Discuss]

HOME Back

  学了一发动态维护凸包... 所以说算是一个增量法? 每次找到前后趋往两边删, 判断就用叉积判断就行了. 至于前后趋, 插入删除的用个set就好了. 用两个set分别维护上凸壳和下凸壳即可.

  这道题只用维护上凸壳就可以了... 还算是十分的好写的. 删除操作不会, 但是发现只有删除操作, 于是可以离线倒过来变成加点操作, 这就很棒棒了...

  关于复杂度... 虽然平衡树是log的, 但是每次往两边删... 那我也不会证了(听说凸包上的点数期望很少?

  Upd: 突然想起来如果只是增加的话, 那么一个点最多被加入和删除一次!! 均摊nlogn.

  还有讲真删除操作怎么搞啊(不离线的话)(实际上又插入又删除也能卡掉离线).

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
int n, m, Q;
double ans;
bool vis[maxn];
inline const int read() {
    register int x = 0;
    register char ch = getchar();
    while (ch < '0' || ch > '9') ch = getchar();
    while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
    return x;
}
struct query {
    int x, opt; double ans;
}q[maxn];
struct point {
    int x, y;
    point(){}
    point(int x, int y) : x(x), y(y) {}
    inline bool operator < (const point &s) const {
        return x < s.x || (x == s.x) && y < s.y;
    }
    inline point operator - (const point &s) const {
        return point(x - s.x, y - s.y);
    }
    inline double operator + (const point &s) const {
        return sqrt((double)(x - s.x) * (x - s.x) + (y - s.y) * (y - s.y));
    }
    inline double operator * (const point &s) const {
        return x * s.y - y * s.x;
    }
}a[maxn], p1, p2, p3;
set<point> s;
set<point>::iterator l, r, it;
inline void insert(point p) {
    r = s.lower_bound(p), l = r;
    l --;
    if ((*r - *l) * (p - *l) < 0) return;
    ans -= (*l) + (*r);
    while (true) {
        it = r; r ++;
        if (r == s.end()) break;
        if ((*r - p) * (*it - p) > 0) break;
        ans -= (*it) + (*r);
        s.erase(it);
    }
    while (l != s.begin()) {
        it = l; l --;
        if ((*it - p) * (*l - p) > 0) break;
        ans -= (*l) + (*it);
        s.erase(it);
    }
    s.insert(p);
    l = r = it = s.find(p);
    l --, r ++;
    ans += ((*l) + (*it)) + ((*it) + (*r));
}
int main() {
    n = read();
    p1.x = 0, p1.y = 0, s.insert(p1);
    p2.x = n, p2.y = 0, s.insert(p2);
    int x = read(), y = read();
    p3.x = x, p3.y = y, s.insert(p3);
    ans = (p1 + p3) + (p2 + p3);
    m = read();
    for (int i = 1; i <= m; ++ i) a[i].x = read(), a[i].y = read();
    Q = read();
    for (int i = 1; i <= Q; ++ i) {
        q[i].opt = read();
        if (q[i].opt & 1) q[i].x = read(), vis[q[i].x] = true;
    }
    for (int i = 1; i <= m; ++ i)
        if (!vis[i]) insert(a[i]);
    for (int i = Q; i; -- i) {
        if (q[i].opt & 1) insert(a[q[i].x]);
        else q[i].ans = ans;
    }
    for (int i = 1; i <= Q; ++ i)
        if (q[i].opt == 2) printf("%.2f\n", q[i].ans);
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值