• [XJOI NOI02015训练题7] B 线线线 【二分】


    题目链接:XJOI - NOI2015-07 - B

    题目分析

    题意:过一个点 P 的所有直线,与点集 Q 的最小距离是多少?一条直线与点集的距离定义为点集中每个点与直线距离的最大值。

    题解:二分答案,对于一个二分的距离,我们可以求出对于每个点的可用的极角范围,然后判断 n 个点的极角范围有没有交即可。

    听起来非常简单..结果我发现细节很麻烦..

    因为,极角的范围是环形的,如果限定在 [-PI, PI] 的范围内,跨越 -PI = PI 这条线的极角范围就很难处理。

    然后两个环上的范围的交可能是两段,也是很难处理..

    学习神犇的处理方式,对于每个极角范围,在左端点记上一个 1,右端点记上一个 -1,然后如果一个位置被 n 个区间包含,那么这个位置的前缀和就是 n 。

    非常的和谐,看起来问题已经解决了...然而我发现神犇的做法还是有些细节无法理解..

    比如区间的范围可能超出了 [-PI, PI] ....但是我已经想不清楚了...还是记住这种处理方式吧

    照着神犇的代码写之后还是 TLE 了 2 个点,最后改了改 Eps 让二分次数减少了一些,终于过了。

    并且向下保留 3 位小数,我这样写 printf("%.3f ", Ans - 0.0005); 就会 WA 掉 1 个点。

    这样写 AnsN = (int)(Ans * 1000); printf("%d.%03d ", AnsN / 1000, AnsN % 1000); 才能 AC。

    代码

    #include <iostream>
    #include <cstdlib>
    #include <cstdio>
    #include <cmath>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    
    #define Vector Point
    #define PI 3.14159265358979
    
    inline void Read_Int(int &Num)
    {
    	char c = getchar();
    	bool Neg = false;	
    	while (c < '0' || c > '9')
    	{
    		if (c == '-') Neg = true;
    		c = getchar();
    	}
    	Num = c - '0'; c = getchar();
    	while (c >= '0' && c <= '9')
    	{
    		Num = Num * 10 + c - '0';
    		c = getchar();
    	}
    	if (Neg) Num = -Num;
    }
    
    typedef double LF;
    
    inline LF gmin(LF a, LF b) {return a < b ? a : b;}
    inline LF gmax(LF a, LF b) {return a > b ? a : b;}
    inline LF Sqr(LF x) {return x * x;}
    
    const LF Eps = 1e-6;
    
    const int MaxN = 111111 + 5;
    
    int n, Top, Tot;
    
    LF dis[MaxN], ta[MaxN];
    
    struct ES
    {
    	LF Pos;
    	int Num;
    	ES() {}
    	ES(LF a, int b) {Pos = a; Num = b;}
    } EQ[MaxN * 4];
    
    inline bool Cmp(ES e1, ES e2)
    {
    	return e1.Pos < e2.Pos;
    }
    
    struct Point
    {
    	LF x, y;
    	Point() {}
    	Point(LF a, LF b) {x = a; y = b;}
    	
    	void Read()
    	{
    		int a, b;
    		Read_Int(a); Read_Int(b);
    		x = (LF)a; y = (LF)b;
    	}
    } Px, P[MaxN];
    
    inline LF Dis(Point p1, Point p2)
    {
    	return sqrt(Sqr(p1.x - p2.x) + Sqr(p1.y - p2.y));
    }
    
    bool Check(LF d)
    {
    	LF l, r, t;
    	Top = 0; Tot = n;
    	for (int i = 1; i <= n; ++i)
    	{
    		if (dis[i] <= d) 
    		{	
    			--Tot;
    			continue;
    		}
    		t = asin(d / dis[i]);
    		l = ta[i] - t;
    		r = ta[i] + t;
    		EQ[++Top] = ES(l, 1);
    		EQ[++Top] = ES(r, -1);
    		if (ta[i] > 0)
    		{
    			EQ[++Top] = ES(l - PI, 1);
    			EQ[++Top] = ES(r - PI, -1);
    		}
    		else
    		{
    			EQ[++Top] = ES(l + PI, 1);
    			EQ[++Top] = ES(r + PI, -1);
    		}
    	}
    	if (Top == 0) return true;
    	int Cnt = 0;
    	sort(EQ + 1, EQ + Top + 1, Cmp);
    	for (int i = 1; i <= Top; ++i)
    	{
    		Cnt += EQ[i].Num;
    		if (Cnt == Tot) return true;
    	}
    	return false;
    }
    
    int main()
    {
    	scanf("%d", &n);
    	Px.Read(); 
    	for (int i = 1; i <= n; ++i) 
    	{
    		P[i].Read();
    		dis[i] = Dis(P[i], Px);
    		ta[i] = atan2(P[i].y - Px.y, P[i].x - Px.x);
    	}
    	LF l, r, mid, Ans;
    	l = 0; r = 2000000;
    	while (r - l >= Eps)
    	{
    		mid = (l + r) / 2.0;
    		if (Check(mid))
    		{
    			Ans = mid;
    			r = mid - Eps;
    		}
    		else l = mid + Eps;
    	}
    	int AnsN = (int)(Ans * 1000);
    	printf("%d.%03d
    ", AnsN / 1000, AnsN % 1000);
    	return 0;
    }
    

      

  • 相关阅读:
    文件下载链接
    Python Web服务器
    打印字符串替换
    python 将数据写入excel
    python Telnet通讯
    Python 串口通讯
    monkey 原理,环境搭建、命令详解
    Andriod 四大组件,六大布局
    python练习题100例
    Activity的基本概念与Activity的生命周期
  • 原文地址:https://www.cnblogs.com/JoeFan/p/4593033.html
Copyright © 2020-2023  润新知