[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;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章