• 「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;
    }
    
  • 相关阅读:
    [Wap] 制作自定义WmlListAdapter来实现Mobile.List控件的各种效果
    [EntLib]UAB(Updater Application Block)下载
    jarhoo是一个很棒的地方
    [GoogleMap]利用GoogleMap地图的这个应用真是太狠了[1]
    [J2ME] VideoCoolala(MobileWebCam)开源说明
    [p2p]手机是否可以通过JXTA网络与PC机/PocketPC/WindowsMobile实现P2P呢?
    Android Layout XML属性
    什么是9.png
    android主流UI布局
    Android开发之旅: Intents和Intent Filters(理论部分)
  • 原文地址:https://www.cnblogs.com/mangoyang/p/10118441.html
Copyright © 2020-2023  润新知