AGC039D 题解

题目描述

给定在笛卡尔坐标系的单位圆上的\(N\)个点(圆心为\((0, 0)\))。第\(i\)个点的坐标为\((cos(\frac{2 \pi T_i}{L}), sin(\frac{2 \pi T_i}{L}))\)

三个不同的点将在这\(N\)个点中等概率的随机,请求出这三个点构成的三角形的内切圆圆心的\(x\)坐标的数学期望和\(y\)坐标的数学期望。

约束条件

\(3 \leq N \leq 3000\)

\(N \leq L \leq 10^9\)

\(0 \leq T_i \leq L - 1\)

\(T_i < T_{i+1}\)

所有的输入的数都是整数。

测试点时间限制:4s

测试点空间限制:1024MB

题目解答

算法一

解题过程

首先,我们考虑一种内心的刻画方法(这种刻画方法在数学竞赛中被称为"鸡爪定理")。

\(\triangle ABC\) 的内心为\(I\)\(AI\)\(\triangle\) \(ABC\)的外接圆交于另一点\(M\),则\(BM = CM = IM\)

证明:由于$ \angle BAM = \angle CAM\(,故\) \angle BCM = \angle CBM\(,所以\)BM = CM\(。又因为\)\angle IBM = \angle CBM + \angle IBC = \frac{1}{2}(\angle CAB + \angle ABC) = \angle MAB + \angle ABI = \angle BIM\(,所以\)BM = MI$

Alt text

(之后看这张图的时候\(B,C\)可能需要互换一下)

这样,如果我们固定了\(B,C\)两点,以及\(A\)\(B,C\)与圆的哪一段弧上,我们就可以得到弧\(BC\)的中点\(M\)\(M\)\(A\)\(BC\)异侧)。我们不能枚举\(A\)点,但是我们将\(I\)刻画为:\(M + (B - M) \cdot e^{i \angle AMB}\)。(解释:\((I - M) = (B - M) \cdot (\cos \angle AMB + i \sin \angle AMB)\))(这里我们使用了复平面的工具),那么\(M\)是固定的,\(B - M\)是固定的(即与\(A\)无关)。要求所有\(I\)的坐标之和,只需要知道\(e^{i \angle AMB}\)的和。虽然这个式子与\(A,B\)有关,但是与\(M,C\)均无关(圆周角相等)。

因此,我们先枚举\(B\),接着逆时针顺序枚举\(C\),在枚举的过程中顺便维护

<1>\(B\)逆时针到\(C\)的点\(A\)的数目。

<2> 对于从\(B\)逆时针到\(C\)经过的点\(A\),维护\(e^{i \angle AMB}\)的和(这个和与\(M\)的位置是无关的)。

如果我们固定了\(B,C\)的话,以及\(A\)\(B,C\)与圆的哪一段弧上,可以算出\(M\)的值,也可以算出\(M\)对答案的贡献的次数。同时,\(B - M\)对每一个\(A\)是一样的,而\(e^{i \angle AMB}\)的和又是被维护出来的。这样,我们就可以以\(O(n^2)\)的复杂度算出内心的坐标和了。但是,每个内心被算了三次,而且我们最终答案是内心横纵坐标的期望,所以要将答案除以\(\frac{n(n - 1)(n - 2)}{2}\)

算法二

我们对称地考虑三个弧中点构成的三角形。设这三个点\(D,E,F\)对应的复数也是\(D,E,F\),则我们通过计算角度发现内心对于的复数就是\(D + E + F\)

证明1:这个三角形的重心为\(\frac{D + E + F}{3}\),并且由欧拉线定理及比例关系容易得到垂心\(H = D + E + F\)

证明2: 只需证明\(H' = D + E + F\)时,\((H' - D)\)\((E - F)\)垂直,而\((\vec{OE} + \vec{OF}) \cdot (\vec{OF} - \vec{OE}) = \lvert \vec{OF} \rvert ^2 - \lvert \vec{OE} \rvert ^2 = 0\),故\(D + E + F\)\(\triangle DEF\)的垂心。

得到了这个结论过后,我们枚举两个点,以及它们对应的一段弧,计算出这段弧的中点以及不在这段弧上的点的个数,就可以得到这段弧中点的复数值在最终答案里面出现的次数,再将它们相加即可。

时间复杂度仍然为\(O(n^2)\)

代码实现


#include <bits/stdc++.h>
using namespace std;

const int N = 3005;
const double PI = acos(-1), eps = 1e-10;

int n, L;
double alpha[N], ansx = 0.0, ansy = 0.0;

double midpoint (double x1, double x2) {
	double len = x2 - x1;
	if (len < -eps) len += 2.0 * PI;
	len /= 2.0;
	double mid = x1 + len;
	if (mid >= 2.0 * PI - eps) mid -= 2.0 * PI;
	return mid;
}

int main () {
	scanf("%d%d", &n, &L);
	for (int i = 0; i < n; i++) {
		int x;
		scanf("%d", &x);
		alpha[i] = 2.0 * PI * x / L;
	}

	for (int i = 0; i < n; i++) {
		double nowx = 0.0, nowy = 0.0;
		for (int j = (i + 1) % n, k = 0; j != i; j = (j + 1) % n, k++) {
			double m = midpoint(alpha[j], alpha[i]), arg = alpha[j] - alpha[i];
			double vecx = cos(alpha[i]) - cos(m), vecy = sin(alpha[i]) - sin(m);

			ansx += cos(m) * k, ansy += sin(m) * k;
			ansx += vecx * nowx - vecy * nowy, ansy += vecx * nowy + vecy * nowx;

			if (arg < -eps) arg += 2.0 * PI;
			nowx += cos(arg / 2.0), nowy += sin(arg / 2.0);
		}
	}

	ansx /= 0.5 * n * (n - 1) * (n - 2), ansy /= 0.5 * n * (n - 1) * (n - 2);
	printf("%.10lf %.10lf", ansx, ansy);
	return 0;
}

posted @ 2020-03-18 07:57  unzcjouhi  阅读(338)  评论(0编辑  收藏  举报