1. 程式人生 > >「APIO2018選圓圈」

「APIO2018選圓圈」

「APIO2018選圓圈」

題目描述

在平面上,有 \(n\) 個圓,記為 \(c_1, c_2, \ldots, c_n\) 。我們嘗試對這些圓執行這個演算法:

  1. 找到這些圓中半徑最大的。如果有多個半徑最大的圓,選擇編號最小的。記為 \(c_i\)
  2. 刪除 \(c_i\) 及與其有交集的所有圓。兩個圓有交集當且僅當平面上存在一個點,這個點同時在這兩個圓的圓周上或圓內。
  3. 重複上面兩個步驟直到所有的圓都被刪除。

當 \(c_i\) 被刪除時,若迴圈中第1步選擇的圓是 \(c_j\) ,我們說 \(c_i\)

 被 \(c_j\) 刪除。對於每個圓,求出它是被哪一個圓刪除的。

越抓越癢有理想,\(n^2\) 有信仰。

首先把圓心拿出來建KD樹,對於每一棵子樹,維護出一個矩形框住這些圓的並,每次刪除爆枚整棵樹通過判斷和這個矩形有沒有交來剪枝,最壞複雜度 \(O(n^2)\) ,好像旋轉某個角度就卡不掉了。

話說寫APIO題卡評測真是爽啊,順便寫篇部落格存個KD樹板子。

解題思路 :

/*program by mangoyang*/
#include<bits/stdc++.h>
#define inf (0x7f7f7f7f)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
    int f = 0, ch = 0; x = 0;
    for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
    for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
    if(f) x = -x;
}
const int N = 600005;
const double sina = sqrt(2)/2, cosa = sina, eps = 5e-2;

int ans[N], Sgn, rt, n;

inline double sqr(double x){ return x * x; }
inline void chkmax(double &x, double y){ if(y > x) x = y; }
inline void chkmin(double &x, double y){ if(y < x) x = y; }
struct Point{ double x, y, r; int id; } a[N], s[N];
inline bool cmp(Point A, Point B){
    if(Sgn == 0) return A.x < B.x;
    if(Sgn == 1) return A.y < B.y;
    if(Sgn == 2) return A.r == B.r ? (A.id < B.id) : (A.r > B.r);
}
struct Kdtree{
    int ch[N][2];
    struct Node{
        double mx[2], mn[2]; int id;
    }T[N];
    inline void update(int x){
        T[x].mx[0] = a[x].x + a[x].r, T[x].mx[1] = a[x].y + a[x].r;
        T[x].mn[0] = a[x].x - a[x].r, T[x].mn[1] = a[x].y - a[x].r;
        for(int i = 0; i < 2; i++) if(ch[x][i])
            for(int j = 0; j < 2; j++){
                chkmax(T[x].mx[j], T[ch[x][i]].mx[j]);
                chkmin(T[x].mn[j], T[ch[x][i]].mn[j]);
            }
    }
    inline void build(int &u, int l, int r, int sgn){
        if(l > r) return;
        int mid = l + r >> 1; u = mid, Sgn = sgn;
        nth_element(a + l, a + mid, a + r + 1, cmp);
        build(ch[u][0], l, mid - 1, sgn ^ 1);
        build(ch[u][1], mid + 1, r, sgn ^ 1), update(u);
    }
    inline void Delete(int u, Point now){
        if(!u || now.x - now.r > T[u].mx[0] || now.x + now.r < T[u].mn[0]
        || now.y - now.r > T[u].mx[1] || now.y + now.r < T[u].mn[1]) return;
        if(!ans[a[u].id]){
            if(sqr(now.x-a[u].x)+sqr(now.y-a[u].y) <= sqr(now.r+a[u].r) + eps)
                ans[a[u].id] = now.id, a[u].x = a[u].y = 0, a[u].r = -inf;
        }
        Delete(ch[u][0], now), Delete(ch[u][1], now), update(u);
    }
}van;
int main(){
    read(n);
    for(int i = 1, x, y; i <= n; i++){
        read(x), read(y), read(a[i].r), a[i].id = i;
        a[i].x = (double) x * cosa - y * sina;
        a[i].y = (double) x * sina + y * cosa;
    }
    Sgn = 2, sort(a + 1, a + n + 1, cmp);
    for(int i = 1; i <= n; i++) s[i] = a[i];
    van.build(rt, 1, n, 0);
    for(int i = 1; i <= n; i++)
        if(!ans[s[i].id]) van.Delete(rt, s[i]);
    for(int i = 1; i <= n; i++) printf("%d ", ans[i]);
    return 0;
}