Project Euler 842

Project Euler 842

题目

Irregular Star Polygons

Given \(n\) equally spaced points on a circle, we define an \(n\)-star polygon as an \(n\)-gon having those \(n\) points as vertices. Two \(n\)-star polygons differing by a rotation/reflection are considered different.

For example, there are twelve \(5\)-star polygons shown below.

For an \(n\)-star polygon \(S\), let \(I(S)\) be the number of its self intersection points.

Let \(T(n)\) be the sum of \(I(S)\) over all \(n\)-star polygons \(S\).

For the example above \(T(5) = 20\) because in total there are \(20\) self intersection points.

Some star polygons may have intersection points made from more than two lines. These are only counted once. For example, \(S\), shown below is one of the sixty \(6\)-star polygons. This one has \(I(S) = 4\).

You are also given that \(T(8) = 14640\).

Find \(\displaystyle \sum_{n = 3}^{60}T(n)\). Give your answer modulo \((10^9 + 7)\).

解决方案

任何自交点都来自两条(或多条)弦在内部相交;而所有可能的弦都属于完全图 \(K_n\) 的连线(边界边不会在内部与其他边相交)。因此可以先研究“正 \(n\) 边形所有对角线的交点集合”。

把一个内部交点记为 \(P\)。设恰有 \(k\) 条对角线通过 \(P\)\(k\ge 2\)),并且这些对角线两两都在 \(P\) 相交。对某个多边形 \(S\),点 \(P\) 会被计入 \(I(S)\) 当且仅当 \(S\) 的边集中包含了至少两条经过 \(P\) 的对角线;即在这 \(k\) 条对角线里,\(S\) 选中了 \(\ge 2\) 条。

于是 \[ T(n)=\sum_{P}\#\{S:|\mathcal{D}(P)\cap E(S)|\ge 2\}. \]

其中 \(\mathcal{D}(P)\) 表示所有经过交点 \(P\) 的对角线集合,\(E(S)\) 表示多边形 \(S\) 的边集合。

因此关键变成两个问题:

  1. 对给定的 \(k\),求 一个 \(k\)-重交点\(T(n)\) 的贡献 \(A(k,n)\)
  2. 对每个 \(n\),求内部交点里“恰有 \(k\) 条对角线共点”的点数 \(a_k(n)\)

先做一个基本计数:给定 \(r\) 条两两不共享端点的边(这里是对角线),问有多少个 \(n\)-star polygon(无向哈密顿圈)包含它们全部?可见,将每条必选边 \({u_i,v_i}\) 收缩成一个“超点”,则顶点数从 \(n\) 变为 \(n-r\)。在收缩后的图上,一个无向哈密顿圈数为 \(\dfrac{(n-r-1)!}{2}\)。展开回原图时,每条收缩边在圈中有两种穿越方向(\(u_i\to v_i\)\(v_i\to u_i\)),独立给出 \(2^r\) 倍。故包含这 \(r\) 条边的 \(n\)-star polygon 数为 \[ F(r,n)=2^r\cdot \frac{(n-r-1)!}{2}=2^{r-1}(n-r-1)!. \] 接下来会用到:若一个交点 \(P\) 上有 \(k\) 条对角线,则任取其中 \(r\) 条,端点必然互不重合(否则会在顶点处相交而不是内部相交),因此上式适用。

固定一个交点 \(P\),设通过它的对角线集合为 \(\mathcal{D}\),且\(|\mathcal{D}|=k\)

对一条多边形 \(S\),设它选中了其中 \(s\) 条对角线(\(0\le s\le k\))。我们希望这个点对 \(S\) 的贡献是\(\displaystyle{w(s)=\begin{cases}0,& s=0,1,\\1,& s\ge 2.\end{cases}}\)

我们构造一个恒等式是:对所有整数 \(s\ge 0\),有\(\displaystyle{\mathbf{1}\{s\ge 2\}=\sum_{r=2}^{s}(-1)^r(r-1)\binom{s}{r}.}\)验证很直接:令\(\displaystyle{S_0(s)=\sum_{r=0}^s(-1)^r\binom{s}{r}=(1-1)^s,S_1(s)=\sum_{r=0}^s(-1)^r r\binom{s}{r}= s(1-1)^{s-1}.}\)

则当 \(s\ge 2\)\(S_0(s)=S_1(s)=0\),整理\(\displaystyle{\sum_{r=2}^s(-1)^r(r-1)\binom{s}{r}=\Big(\sum_{r=0}^s(-1)^r r\binom{s}{r}\Big)-\Big(\sum_{r=0}^s(-1)^r\binom{s}{r}\Big)+1=1.}\)\(s=0,1\) 时左侧为空和为 \(0\),恒等式成立。

因此,点 \(P\)\(T(n)\) 的总贡献为\(\displaystyle{A(k,n)=\sum_{S} w(s(S))=\sum_{S}\sum_{r=2}^{s(S)}(-1)^r(r-1)\binom{s(S)}{r}.}\)交换求和并按“选择哪 \(r\) 条对角线都对称”:\(\displaystyle{A(k,n)=\sum_{r=2}^{k}(-1)^r(r-1)\binom{k}{r}\cdot F(r,n).}\)代入上面的 \(F(r,n)=2^{r-1}(n-r-1)!\),得到关键公式:

\[ A(k,n)=\sum_{r=2}^{k}(-1)^r(r-1)\binom{k}{r}2^{r-1}(n-r-1)!. \]

\(a_k(n)\) 为正 \(n\) 边形对角线交点中(不含中心点)恰有 \(k\) 条对角线共点的点数。这篇论文证明(并给出显式tame的表达式):对所有 \(k\ge 8\),都有 \(a_k(n)=0\),因此除中心外最多只有 \(7\) 条对角线共点。并给出 \(a_2(n),\dots,a_7(n)\) 的显式公式(以 \(\delta_m(n)=\mathbf{1}\{m\mid n\}\) 表示整除指示函数)。

最终,总交点数满足\(\displaystyle{I(n)=\delta_2(n)+\sum_{k=2}^7 a_k(n),}\)其中 \(\delta_2(n)\) 正是为了在 \(n\) 为偶数时补上中心点。这也反映了中心点是唯一可能出现 \(>7\) 条对角线共点的位置。

因此实现时直接用 \(\delta_m(n)\) 分段即可。对于中心点的处理:当 \(n\) 为偶数时,中心有且仅有一个交点,且有 \(k_0=n/2\) 条直径共点;因此在求 \(T(n)\) 时额外加上一次 \(A(n/2,n)\)

于是最终得到

\[ T(n)=\sum_{k=2}^{7} a_k(n)A(k,n)+\mathbf{1}\{2\mid n\}\cdot A\left(\frac n2,n\right). \]

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
from tools import Combinatorics

N = 60
MOD = 1_000_000_007


def eval_poly(coeffs, n, mod):
x = n % mod
v = 0
for c in coeffs:
v = (v * x + c) % mod
return v


TERMS = {
2: [
(1, [1, -6, 11, -6], 24),
(2, [-5, 46, -72], 16),
(4, [-9], 4),
(6, [-19, 110], 2),
(12, [54], 1),
(18, [84], 1),
(24, [50], 1),
(30, [-24], 1),
(42, [-100], 1),
(60, [-432], 1),
(84, [-204], 1),
(90, [-144], 1),
(120, [-204], 1),
(210, [-144], 1),
],
3: [
(2, [5, -48, 76], 48),
(4, [3], 4),
(6, [7, -38], 6),
(12, [-8], 1),
(18, [-20], 1),
(24, [-16], 1),
(30, [-19], 1),
(42, [8], 1),
(60, [68], 1),
(84, [60], 1),
(90, [48], 1),
(120, [60], 1),
(210, [48], 1),
],
4: [
(6, [7, -42], 12),
(12, [-5], 2),
(18, [-4], 1),
(24, [3], 1),
(42, [6], 1),
(60, [34], 1),
(84, [-6], 1),
(120, [-6], 1),
],
5: [
(6, [1, -6], 4),
(12, [-3], 2),
(24, [-2], 1),
(42, [4], 1),
(84, [6], 1),
(120, [6], 1),
],
6: [
(30, [4], 1),
(60, [-4], 1),
],
7: [
(30, [1], 1),
(60, [4], 1),
],
}


def solve(N=60):
combi = Combinatorics(max_n=N, mod=MOD)

inv = {}
for d in [1, 2, 4, 6, 12, 16, 24, 48]:
inv[d] = pow(d, -1, MOD)

pow2 = [1] * (N + 1)
for i in range(1, N + 1):
pow2[i] = pow2[i - 1] * 2 % MOD

def ak(n, k):
s = 0
for m, poly, den in TERMS[k]:
if m == 1 or n % m == 0:
s = (s + eval_poly(poly, n, MOD) * inv[den]) % MOD
return (n % MOD) * s % MOD

def A_point(k, n):
res = 0
for r in range(2, k + 1):
if n - r - 1 < 0:
break
term = combi.C(k, r) * (r - 1) % MOD
term = term * pow2[r - 1] % MOD
term = term * combi.fact[n - r - 1] % MOD
if r & 1:
res = (res - term) % MOD
else:
res = (res + term) % MOD
return res

def T(n):
res = 0
for k in range(2, 8):
v = ak(n, k)
if v:
res = (res + v * A_point(k, n)) % MOD
if n % 2 == 0:
res = (res + A_point(n // 2, n)) % MOD
return res

ans = 0
for n in range(3, N + 1):
ans = (ans + T(n)) % MOD
return ans

print(solve(N))