计算几何专题 —— 极角序H Nearest Point

题目大意求每个点是枚举点的最近点的概率是多少。转化一下就是一个平面中多少的弧度属于这个点最后除以2π。最近点在旋转的过程中会有如下变化: alt 可以发现最近点的转变是在某一关键位置发现了转变,只需要记录关键位置即可。即枚举点A,观察B,C点对于不同旋转角度,谁在x轴上的投影离A更近 旋转x轴,发现 1.x轴垂直于BC 2.设BC中点为D,x轴垂直于AD 共4种情况,BC的离A的远近关系发生变化 之后将关键位置进行极角排序,每次拿到相邻的两个向量,求得这两个向量之间是属于哪个点的区域。最后将这个点的区域加上弧度即可。至于如何求弧度以及如何判定谁更近有以下操作: alt

核心代码:

using point = Point<int>;
void solve()
{
	cout << fixed << setprecision(12);
	int n;
	cin >> n;
	vector<point> p(n);
	for(int i = 0 ; i < n ; i ++)
	{
		cin >> p[i];
		p[i] = p[i] * 2;//扩大两倍防止取中点有误差
	}

	auto work = [&](point &o , int id) -> void{
		vector<point> v;
		int idx = 0 , dis = 1e9;
		for(int i = 0 ; i < n ; i ++)
		{
			if(i == id) continue;
			if(dis > o.dis2(p[i]))
			{
				dis = o.dis2(p[i]);
				idx = i;
			}
			for(int j = i + 1 ; j < n ; j ++)
			{
				if(i == id || j == id) continue;
				point BC = p[i]&p[j];//pipj
				point AD = o&((p[i] + p[j])/2);
				if((AD ^ BC) == 0) continue;
              	//cos sin
				v.push_back(BC.rot(0,1));
				v.push_back(BC.rot(0,-1));
				v.push_back(AD.rot(0,1));
				v.push_back(AD.rot(0,-1));
			}
		}
		if(!v.size())
		{
			for(int i = 0 ; i < n ; i ++)
				cout << (idx == i) << " \n"[i==n-1];
			return;
		}
		sort(v.begin(),v.end(),argcmp<int>());
		v.push_back(v[0]);
		vector<double> ans(n);
		int m = v.size();//m最坏是n^2
		for(int i = 0 ; i + 1 < m ; i ++)
		{
			int j = i + 1;
			point w = v[i] + v[j];
			int cur = 1e9 , idx = -1;
			for(int k = 0 ; k < n ; k ++)
			{
				if(k == id) continue;
				if(cur > abs(w*(o&p[k])))
				{
					cur = abs(w*(o&p[k]));
					idx = k;
				}
			}
			double alpha = atan2l(v[i]^v[j],v[i]*v[j]);
			ans[idx] += alpha;
		}
		for(int i = 0 ; i < n ; i ++)
		{
			if(i == id) cout << 0 << " ";
			else cout << ans[i] / (PI*2)<< " ";
		}
		cout << endl;
	};	

	for(int i = 0 ; i < n ; i ++)
	{
		work(p[i],i);
	}
}