Problem Summary

Let \(G_N=\{(x,y)\in\mathbb{Z}^2:0\le x,y\le N\}\). A finite set \(S\subseteq G_N\) is called titanic if some line passes through exactly two points of \(S\). We must compute \(T(N)\), the number of titanic subsets of \(G_N\), modulo \(10^8\).

The implementation never enumerates subsets directly. Instead, it counts the complement: empty sets, singletons, and larger subsets whose selected points are all collinear.

Mathematical Approach

Step 1: Reduce to the Complement

The key theorem is Sylvester-Gallai: every finite non-collinear set of real points has an ordinary line, meaning a line containing exactly two of the points. Therefore a lattice subset is not titanic if and only if it is empty, a singleton, or all of its points lie on one common line.

If \(\operatorname{NT}(N)\) denotes the number of non-titanic subsets, then

$$T(N)=2^{(N+1)^2}-\operatorname{NT}(N)\pmod{10^8}.$$

Step 2: Replace Subset Counting by Line-Length Counting

Suppose a grid line contains exactly \(k\) lattice points. The non-titanic subsets supported on that line are exactly the subsets of size at least \(3\), so that line contributes

$$\sum_{r=3}^{k}\binom{k}{r}=2^k-1-k-\binom{k}{2}.$$

Now define \(F_\ell(N)\) as the number of grid lines containing at least \(\ell+1\) lattice points. For a fixed line with \(k\) points, one has the identity

$$\sum_{\ell=2}^{k-1}\bigl(2^\ell-(\ell+1)\bigr)=2^k-1-k-\binom{k}{2}.$$

Therefore

$$\operatorname{NT}(N)=1+(N+1)^2+\sum_{\ell=2}^{N}F_\ell(N)\bigl(2^\ell-(\ell+1)\bigr).$$

The whole task is now reduced to computing \(F_\ell(N)\) efficiently for all \(\ell\ge2\).

Step 3: Primitive Directions and Segment Placements

Write \(a=N+1\). Every non-axis grid line has a primitive step vector \((u,v)\) with \(1\le v\le u\) and \(\gcd(u,v)=1\). The pair \((u,v)\) represents the slope classes \(\pm v/u\), and when \(u\ne v\), also the swapped classes \(\pm u/v\). Horizontal and vertical lines are handled separately.

For such a primitive direction, the number of placements of an \(\ell\)-step segment inside the square is

$$C_\ell(u,v)=\max(a-\ell u,0)\max(a-\ell v,0).$$

If a line contains \(k\) lattice points, then it contributes exactly \(k-\ell\) segments of length \(\ell\) and \(k-\ell-1\) segments of length \(\ell+1\). Their difference is \(1\) exactly when \(k\ge\ell+1\). Hence the number of distinct lines in this direction class that contain at least \(\ell+1\) points is

$$C_\ell(u,v)-C_{\ell+1}(u,v).$$

After doubling for the two signs and adding the \(a\) horizontal plus \(a\) vertical lines, we recover \(F_\ell(N)\).

Step 4: Totient-Based Direction Statistics

Let

$$\Phi(M)=\sum_{d\le M}\varphi(d),\qquad \Psi_1(M)=\sum_{d\le M} d\,\varphi(d),\qquad \Psi_2(M)=\sum_{d\le M} d^2\varphi(d).$$

The implementation stores the primitive-direction information in three cumulative quantities:

$$c(M)=2\Phi(M)-1,\qquad s_{xy}(M)=3\Psi_1(M)-1,\qquad s_{\prod}(M)=\Psi_2(M).$$

Why do these formulas appear? Group primitive directions by their larger coordinate \(d\). For fixed \(d>1\), there are \(\varphi(d)\) reduced numerators, their sum is \(d\varphi(d)/2\), and the swapped directions contribute the same amount. The exceptional diagonal case \(d=1\) explains the subtractive constants.

Step 5: Fast Evaluation of \(\Phi\), \(\Psi_1\), and \(\Psi_2\)

The code does not recompute these sums from scratch for every query. Using \(\sum_{d\mid n}\varphi(d)=n\), one gets the divisor-summatory identities

$$\sum_{q=1}^{n}\Phi\!\left(\left\lfloor\frac{n}{q}\right\rfloor\right)=\sum_{m=1}^{n}m=\frac{n(n+1)}{2},$$

$$\sum_{q=1}^{n}q\,\Psi_1\!\left(\left\lfloor\frac{n}{q}\right\rfloor\right)=\sum_{m=1}^{n}m^2=\frac{n(n+1)(2n+1)}{6},$$

$$\sum_{q=1}^{n}q^2\,\Psi_2\!\left(\left\lfloor\frac{n}{q}\right\rfloor\right)=\sum_{m=1}^{n}m^3=\left(\frac{n(n+1)}{2}\right)^2.$$

Those identities lead to memoized recurrences over quotient blocks \(\left\lfloor n/q\right\rfloor\), which is why the summatory functions can be queried many times without a linear scan up to \(n\).

Step 6: Closed Form for \(F_\ell(N)\)

Set

$$M=\left\lfloor\frac{N}{\ell}\right\rfloor,\qquad M'=\left\lfloor\frac{N}{\ell+1}\right\rfloor,\qquad a=N+1.$$

On any interval of \(\ell\) where \(M\) and \(M'\) stay constant, the line count becomes a quadratic polynomial in \(\ell\):

$$F_\ell(N)=2a+2\left(q_2\ell^2+q_1\ell+q_0\right),$$

where

$$q_2=s_{\prod}(M)-s_{\prod}(M'),$$

$$q_1=a\bigl(s_{xy}(M')-s_{xy}(M)\bigr)-2s_{\prod}(M'),$$

$$q_0=a^2\bigl(c(M)-c(M')\bigr)+a\,s_{xy}(M')-s_{\prod}(M').$$

This is exactly the quadratic block formula that appears in the implementation.

Step 7: Block Summation

The values of \(\left\lfloor N/\ell\right\rfloor\) change only \(O(\sqrt N)\) times, so the final summation is performed block by block. For \(\ell\in[L_1,L_2]\), the contribution is

$$\sum_{\ell=L_1}^{L_2}F_\ell(N)\bigl(2^\ell-(\ell+1)\bigr).$$

After substituting the quadratic form, the implementation only needs closed forms for

$$\sum 2^\ell,\qquad \sum \ell 2^\ell,\qquad \sum \ell^2 2^\ell,\qquad \sum \ell,\qquad \sum \ell^2,\qquad \sum \ell^3.$$

The exponential identities used are

$$\sum_{j=0}^{n}2^j=2^{n+1}-1,\qquad \sum_{j=0}^{n}j2^j=(n-1)2^{n+1}+2,$$

$$\sum_{j=0}^{n}j^2 2^j=(n^2-2n+3)2^{n+1}-6.$$

Worked Checkpoint: \(N=4\)

For the \(5\times5\) grid, the formula gives

$$F_2(4)=32,\qquad F_3(4)=16,\qquad F_4(4)=12.$$

Hence

$$\operatorname{NT}(4)=1+25+32(1)+16(4)+12(11)=254,$$

and therefore

$$T(4)=2^{25}-254=33554178,$$

which matches the checkpoint verified by the implementation.

How the Code Works

The C++, Python, and Java implementations follow the same mathematical pipeline. They precompute Euler totients up to a fixed cutoff with a linear sieve, build prefix tables for \(\Phi\), \(\Psi_1\), and \(\Psi_2\), and memoize larger arguments so that each summatory query is solved only once.

After that, the implementation enumerates the distinct quotient blocks of \(\lfloor N/\ell\rfloor\). Inside each block it evaluates the quadratic line-count formula, applies the closed forms for the exponential and polynomial sums, accumulates the complement count, and finally subtracts that value from \(2^{(N+1)^2}\) modulo \(10^8\).

Complexity Analysis

Let \(B\) be the totient precomputation cutoff and let \(Q\) be the number of distinct values taken by \(\lfloor N/\ell\rfloor\); one has \(Q=O(\sqrt N)\). The sieve and prefix tables cost near-linear time and \(O(B)\) memory. The outer summation uses only \(O(Q)\) quotient blocks, and the cached summatory totient queries are shared across those blocks instead of being recomputed. In practice, the heavy work is the prefix-table construction plus the \(O(\sqrt N)\)-scale block arithmetic.

References

  1. Problem page: https://projecteuler.net/problem=415
  2. Sylvester-Gallai theorem: Wikipedia — Sylvester-Gallai theorem
  3. Euler's totient function: Wikipedia — Euler's totient function
  4. Dirichlet hyperbola method: Wikipedia — Dirichlet hyperbola method
  5. Graham, Knuth, Patashnik, Concrete Mathematics, 2nd ed., Addison-Wesley, Chapter 2.

Problemzusammenfassung

Sei \(G_N=\{(x,y)\in\mathbb{Z}^2:0\le x,y\le N\}\). Eine endliche Menge \(S\subseteq G_N\) heißt titanic, wenn es eine Gerade gibt, die genau zwei Punkte aus \(S\) enthält. Gesucht ist also \(T(N)\), die Anzahl titanicer Teilmengen von \(G_N\), modulo \(10^8\).

Die Implementierung durchsucht keine Teilmengen direkt. Stattdessen wird das Komplement gezählt: die leere Menge, alle Einelementmengen und alle größeren Teilmengen, deren Punkte vollständig auf einer gemeinsamen Geraden liegen.

Mathematischer Ansatz

Schritt 1: Auf das Komplement reduzieren

Der entscheidende Satz ist Sylvester-Gallai: Jede endliche nichtkollineare Punktmenge in der Ebene besitzt eine ordinäre Gerade, also eine Gerade mit genau zwei Punkten der Menge. Daher ist eine Gitterteilmenge genau dann nicht titanic, wenn sie leer ist, genau einen Punkt enthält oder vollständig kollinear ist.

Bezeichnet \(\operatorname{NT}(N)\) die Anzahl der nicht-titanicen Mengen, dann gilt

$$T(N)=2^{(N+1)^2}-\operatorname{NT}(N)\pmod{10^8}.$$

Schritt 2: Teilmengen durch Geradenlängen ersetzen

Enthält eine Gittergerade genau \(k\) Gitterpunkte, dann sind genau die Teilmengen dieser Geraden mit mindestens \(3\) Punkten nicht titanic. Ihr Beitrag ist also

$$\sum_{r=3}^{k}\binom{k}{r}=2^k-1-k-\binom{k}{2}.$$

Sei nun \(F_\ell(N)\) die Anzahl der Gittergeraden mit mindestens \(\ell+1\) Gitterpunkten. Für eine feste Gerade mit \(k\) Punkten gilt die Identität

$$\sum_{\ell=2}^{k-1}\bigl(2^\ell-(\ell+1)\bigr)=2^k-1-k-\binom{k}{2}.$$

Also folgt

$$\operatorname{NT}(N)=1+(N+1)^2+\sum_{\ell=2}^{N}F_\ell(N)\bigl(2^\ell-(\ell+1)\bigr).$$

Damit reduziert sich das ganze Problem auf die schnelle Berechnung von \(F_\ell(N)\) für alle \(\ell\ge2\).

Schritt 3: Primitive Richtungen und Segmentplatzierungen

Schreibe \(a=N+1\). Jede nicht horizontale und nicht vertikale Gittergerade besitzt einen primitiven Schrittvektor \((u,v)\) mit \(1\le v\le u\) und \(\gcd(u,v)=1\). Das Paar \((u,v)\) repräsentiert die Steigungsfamilien \(\pm v/u\) und, falls \(u\ne v\), zusätzlich die vertauschten Familien \(\pm u/v\). Horizontale und vertikale Geraden werden separat behandelt.

Für eine solche primitive Richtung ist die Anzahl der Platzierungen eines \(\ell\)-Schritt-Segments im Quadrat

$$C_\ell(u,v)=\max(a-\ell u,0)\max(a-\ell v,0).$$

Hat eine Gerade \(k\) Gitterpunkte, dann liefert sie genau \(k-\ell\) Segmente der Länge \(\ell\) und \(k-\ell-1\) Segmente der Länge \(\ell+1\). Die Differenz ist genau dann \(1\), wenn \(k\ge\ell+1\). Also ist die Anzahl der Geraden dieser Richtungsklasse mit mindestens \(\ell+1\) Punkten gleich

$$C_\ell(u,v)-C_{\ell+1}(u,v).$$

Verdoppelt man für die beiden Vorzeichen und addiert die \(a\) horizontalen sowie \(a\) vertikalen Geraden, erhält man \(F_\ell(N)\).

Schritt 4: Totient-basierte Richtungsstatistiken

Definiere

$$\Phi(M)=\sum_{d\le M}\varphi(d),\qquad \Psi_1(M)=\sum_{d\le M} d\,\varphi(d),\qquad \Psi_2(M)=\sum_{d\le M} d^2\varphi(d).$$

Die Implementierung fasst die primitiven Richtungen in drei kumulativen Größen zusammen:

$$c(M)=2\Phi(M)-1,\qquad s_{xy}(M)=3\Psi_1(M)-1,\qquad s_{\prod}(M)=\Psi_2(M).$$

Diese Formeln entstehen, wenn man nach der größeren Koordinate \(d\) gruppiert. Für festes \(d>1\) gibt es \(\varphi(d)\) teilerfremde Zähler, ihre Summe ist \(d\varphi(d)/2\), und die vertauschten Richtungen liefern denselben Beitrag. Der Sonderfall \(d=1\) erzeugt die konstanten Korrekturterme.

Schritt 5: Schnelle Auswertung von \(\Phi\), \(\Psi_1\) und \(\Psi_2\)

Diese Summen werden nicht für jede Anfrage neu berechnet. Aus \(\sum_{d\mid n}\varphi(d)=n\) folgen die Summenidentitäten

$$\sum_{q=1}^{n}\Phi\!\left(\left\lfloor\frac{n}{q}\right\rfloor\right)=\sum_{m=1}^{n}m=\frac{n(n+1)}{2},$$

$$\sum_{q=1}^{n}q\,\Psi_1\!\left(\left\lfloor\frac{n}{q}\right\rfloor\right)=\sum_{m=1}^{n}m^2=\frac{n(n+1)(2n+1)}{6},$$

$$\sum_{q=1}^{n}q^2\,\Psi_2\!\left(\left\lfloor\frac{n}{q}\right\rfloor\right)=\sum_{m=1}^{n}m^3=\left(\frac{n(n+1)}{2}\right)^2.$$

Daraus entstehen memoiserte Rekursionen über Quotientenblöcke \(\left\lfloor n/q\right\rfloor\). Genau deshalb können diese Summen für große Argumente oft abgefragt werden, ohne bis \(n\) linear zu iterieren.

Schritt 6: Geschlossene Form für \(F_\ell(N)\)

Setze

$$M=\left\lfloor\frac{N}{\ell}\right\rfloor,\qquad M'=\left\lfloor\frac{N}{\ell+1}\right\rfloor,\qquad a=N+1.$$

Auf jedem Intervall von \(\ell\), auf dem \(M\) und \(M'\) konstant bleiben, ist die Geradenzahl ein quadratisches Polynom in \(\ell\):

$$F_\ell(N)=2a+2\left(q_2\ell^2+q_1\ell+q_0\right),$$

wobei

$$q_2=s_{\prod}(M)-s_{\prod}(M'),$$

$$q_1=a\bigl(s_{xy}(M')-s_{xy}(M)\bigr)-2s_{\prod}(M'),$$

$$q_0=a^2\bigl(c(M)-c(M')\bigr)+a\,s_{xy}(M')-s_{\prod}(M').$$

Genau dieses quadratische Blockmodell wird im Programm ausgewertet.

Schritt 7: Blocksummation

Die Werte von \(\left\lfloor N/\ell\right\rfloor\) ändern sich nur \(O(\sqrt N)\)-mal, also wird die Endsumme blockweise berechnet. Für \(\ell\in[L_1,L_2]\) lautet der Beitrag

$$\sum_{\ell=L_1}^{L_2}F_\ell(N)\bigl(2^\ell-(\ell+1)\bigr).$$

Nach Einsetzen des quadratischen Polynoms werden nur noch geschlossene Formen für

$$\sum 2^\ell,\qquad \sum \ell 2^\ell,\qquad \sum \ell^2 2^\ell,\qquad \sum \ell,\qquad \sum \ell^2,\qquad \sum \ell^3$$

benötigt. Verwendet werden insbesondere

$$\sum_{j=0}^{n}2^j=2^{n+1}-1,\qquad \sum_{j=0}^{n}j2^j=(n-1)2^{n+1}+2,$$

$$\sum_{j=0}^{n}j^2 2^j=(n^2-2n+3)2^{n+1}-6.$$

Kontrollbeispiel: \(N=4\)

Für das \(5\times5\)-Gitter liefert die Formel

$$F_2(4)=32,\qquad F_3(4)=16,\qquad F_4(4)=12.$$

Damit folgt

$$\operatorname{NT}(4)=1+25+32(1)+16(4)+12(11)=254,$$

und somit

$$T(4)=2^{25}-254=33554178,$$

genau der Kontrollwert der Implementierung.

Wie der Code arbeitet

Die C++-, Python- und Java-Implementierungen folgen derselben mathematischen Pipeline. Zunächst werden Totientwerte bis zu einer festen Grenze mit einem linearen Sieb vorab berechnet, daraus entstehen Präfixtabellen für \(\Phi\), \(\Psi_1\) und \(\Psi_2\), und für größere Argumente werden die Ergebnisse zwischengespeichert.

Anschließend durchläuft die Implementierung die verschiedenen Quotientenblöcke von \(\lfloor N/\ell\rfloor\). Innerhalb eines Blocks wird die quadratische Formel für die Geradenzahl ausgewertet, dann werden die geschlossenen Summenformeln angewandt, das Komplement akkumuliert und am Ende von \(2^{(N+1)^2}\) modulo \(10^8\) subtrahiert.

Komplexitätsanalyse

Sei \(B\) die Vorberechnungsgrenze für die Totientwerte und \(Q\) die Anzahl der verschiedenen Werte von \(\lfloor N/\ell\rfloor\); es gilt \(Q=O(\sqrt N)\). Sieb und Präfixtabellen kosten nahezu lineare Zeit und \(O(B)\) Speicher. Die äußere Summation verwendet nur \(O(Q)\) Quotientenblöcke, und die großen Summenwerte werden per Cache über diese Blöcke hinweg wiederverwendet. Praktisch dominiert also der Aufbau der Tabellen zusammen mit der \(O(\sqrt N)\)-Blockarithmetik.

Quellen

  1. Problemseite: https://projecteuler.net/problem=415
  2. Sylvester-Gallai-Theorem: Wikipedia — Sylvester-Gallai-Theorem
  3. Eulersche \(\varphi\)-Funktion: Wikipedia — Eulersche Phi-Funktion
  4. Dirichletsche Hyperbelmethode: Wikipedia — Dirichletsche Hyperbelmethode
  5. Graham, Knuth, Patashnik, Concrete Mathematics, 2. Auflage, Addison-Wesley, Kapitel 2.

Problem Özeti

\(G_N=\{(x,y)\in\mathbb{Z}^2:0\le x,y\le N\}\) olsun. Sonlu bir \(S\subseteq G_N\) kümesi, \(S\) içinden tam olarak iki noktadan geçen bir doğru varsa titanic olarak adlandırılır. Amaç, \(G_N\)'nin titanic altkümelerinin sayısı olan \(T(N)\)'yi \(10^8\) modunda hesaplamaktır.

Uygulama altkümeleri tek tek denemez. Bunun yerine tamamlayıcıyı sayar: boş küme, tek noktalı kümeler ve tüm seçili noktaları aynı doğru üzerinde kalan daha büyük altkümeler.

Matematiksel Yaklaşım

Adım 1: Tamamlayıcıya Geçiş

Ana teorem Sylvester-Gallai teoremidir: Düzlemdeki her sonlu ve doğrusal olmayan nokta kümesinin, kümeden tam olarak iki nokta içeren bir ordinary line'ı vardır. Bu nedenle bir kafes altkümesi ancak ve ancak boşsa, tek elemanlıysa ya da tüm noktaları aynı doğru üzerindeyse titanic değildir.

\(\operatorname{NT}(N)\) titanic olmayan altkümelerin sayısı olsun. O zaman

$$T(N)=2^{(N+1)^2}-\operatorname{NT}(N)\pmod{10^8}.$$

Adım 2: Alt Küme Sayımını Doğru Uzunluklarına Dönüştürme

Bir kafes doğrusu tam olarak \(k\) kafes noktası içeriyorsa, bu doğru üzerindeki titanic olmayan seçimler tam olarak en az \(3\) noktalı altkümelerdir. Dolayısıyla bu doğrunun katkısı

$$\sum_{r=3}^{k}\binom{k}{r}=2^k-1-k-\binom{k}{2}$$

olur. Şimdi \(F_\ell(N)\), en az \(\ell+1\) kafes noktası içeren doğru sayısı olsun. \(k\) noktalı tek bir doğru için

$$\sum_{\ell=2}^{k-1}\bigl(2^\ell-(\ell+1)\bigr)=2^k-1-k-\binom{k}{2}$$

eşitliği geçerlidir. Bu nedenle

$$\operatorname{NT}(N)=1+(N+1)^2+\sum_{\ell=2}^{N}F_\ell(N)\bigl(2^\ell-(\ell+1)\bigr).$$

Yani problem, tüm \(\ell\ge2\) için \(F_\ell(N)\)'yi hızlı hesaplamaya indirgenir.

Adım 3: Primitif Yönler ve Parça Yerleşimleri

\(a=N+1\) yazalım. Yatay ve dikey olmayan her kafes doğrusunun, \(1\le v\le u\) ve \(\gcd(u,v)=1\) koşullarını sağlayan bir primitif adım vektörü \((u,v)\) vardır. \((u,v)\) çifti, \(\pm v/u\) eğim sınıflarını ve \(u\ne v\) ise buna ek olarak \(\pm u/v\) sınıflarını temsil eder. Yatay ve dikey doğrular ayrı sayılır.

Bu primitif yön için, kare içindeki \(\ell\) adımlı parça yerleşim sayısı

$$C_\ell(u,v)=\max(a-\ell u,0)\max(a-\ell v,0)$$

şeklindedir. Bir doğru \(k\) kafes noktası içeriyorsa, \(\ell\) uzunluğunda tam \(k-\ell\) parça ve \(\ell+1\) uzunluğunda tam \(k-\ell-1\) parça verir. Aradaki fark ancak \(k\ge\ell+1\) ise \(1\) olur. Demek ki bu yön sınıfında en az \(\ell+1\) nokta içeren doğru sayısı

$$C_\ell(u,v)-C_{\ell+1}(u,v)$$

olur. Bunu iki işaret için ikiyle çarpıp \(a\) yatay ve \(a\) dikey doğruyu ekleyince \(F_\ell(N)\) elde edilir.

Adım 4: Totient Tabanlı Yön İstatistikleri

Aşağıdaki toplamları tanımlayalım:

$$\Phi(M)=\sum_{d\le M}\varphi(d),\qquad \Psi_1(M)=\sum_{d\le M} d\,\varphi(d),\qquad \Psi_2(M)=\sum_{d\le M} d^2\varphi(d).$$

Uygulama primitif yön bilgisini şu birikimli büyüklüklerde toplar:

$$c(M)=2\Phi(M)-1,\qquad s_{xy}(M)=3\Psi_1(M)-1,\qquad s_{\prod}(M)=\Psi_2(M).$$

Bu formüller, yönleri daha büyük koordinatları \(d\) ile gruplayınca ortaya çıkar. Sabit \(d>1\) için \(\varphi(d)\) tane sade pay vardır; bunların toplamı \(d\varphi(d)/2\)'dir ve koordinatları yer değiştirilmiş yönler aynı katkıyı tekrar verir. \(d=1\) olan köşegen durumu ise sabit düzeltme terimlerini açıklar.

Adım 5: \(\Phi\), \(\Psi_1\) ve \(\Psi_2\)'nin Hızlı Hesabı

Bu toplamlar her sorguda baştan hesaplanmaz. \(\sum_{d\mid n}\varphi(d)=n\) kimliğinden

$$\sum_{q=1}^{n}\Phi\!\left(\left\lfloor\frac{n}{q}\right\rfloor\right)=\sum_{m=1}^{n}m=\frac{n(n+1)}{2},$$

$$\sum_{q=1}^{n}q\,\Psi_1\!\left(\left\lfloor\frac{n}{q}\right\rfloor\right)=\sum_{m=1}^{n}m^2=\frac{n(n+1)(2n+1)}{6},$$

$$\sum_{q=1}^{n}q^2\,\Psi_2\!\left(\left\lfloor\frac{n}{q}\right\rfloor\right)=\sum_{m=1}^{n}m^3=\left(\frac{n(n+1)}{2}\right)^2$$

elde edilir. Bunlar, \(\left\lfloor n/q\right\rfloor\) blokları üzerinden çalışan ve önbelleklenen özyinelemeleri doğurur; böylece büyük argümanlar tekrar tekrar doğrusal tarama olmadan hesaplanabilir.

Adım 6: \(F_\ell(N)\) İçin Kapalı Form

Şu tanımları yapalım:

$$M=\left\lfloor\frac{N}{\ell}\right\rfloor,\qquad M'=\left\lfloor\frac{N}{\ell+1}\right\rfloor,\qquad a=N+1.$$

\(M\) ve \(M'\) sabit kaldığı her \(\ell\) aralığında doğru sayısı \(\ell\)'ye göre ikinci dereceden bir polinom olur:

$$F_\ell(N)=2a+2\left(q_2\ell^2+q_1\ell+q_0\right),$$

burada

$$q_2=s_{\prod}(M)-s_{\prod}(M'),$$

$$q_1=a\bigl(s_{xy}(M')-s_{xy}(M)\bigr)-2s_{\prod}(M'),$$

$$q_0=a^2\bigl(c(M)-c(M')\bigr)+a\,s_{xy}(M')-s_{\prod}(M').$$

Uygulamadaki blok formülü tam olarak budur.

Adım 7: Blok Toplamı

\(\left\lfloor N/\ell\right\rfloor\) yalnızca \(O(\sqrt N)\) kez değiştiği için son toplam blok blok alınır. \(\ell\in[L_1,L_2]\) için katkı

$$\sum_{\ell=L_1}^{L_2}F_\ell(N)\bigl(2^\ell-(\ell+1)\bigr)$$

şeklindedir. İkinci derece polinom yerine yazılınca yalnızca

$$\sum 2^\ell,\qquad \sum \ell 2^\ell,\qquad \sum \ell^2 2^\ell,\qquad \sum \ell,\qquad \sum \ell^2,\qquad \sum \ell^3$$

kapalı formlarına ihtiyaç kalır. Kullanılan üstel toplam kimlikleri

$$\sum_{j=0}^{n}2^j=2^{n+1}-1,\qquad \sum_{j=0}^{n}j2^j=(n-1)2^{n+1}+2,$$

$$\sum_{j=0}^{n}j^2 2^j=(n^2-2n+3)2^{n+1}-6$$

biçimindedir.

Doğrulama Örneği: \(N=4\)

\(5\times5\) kafes için formül

$$F_2(4)=32,\qquad F_3(4)=16,\qquad F_4(4)=12$$

değerlerini verir. Dolayısıyla

$$\operatorname{NT}(4)=1+25+32(1)+16(4)+12(11)=254,$$

ve bunun sonucunda

$$T(4)=2^{25}-254=33554178$$

elde edilir; bu da uygulamadaki kontrol değeriyle aynıdır.

Kodun Nasıl Çalıştığı

C++, Python ve Java uygulamaları aynı matematiksel hattı izler. Önce Euler totient değerleri belirli bir sınıra kadar doğrusal elekle hesaplanır, ardından \(\Phi\), \(\Psi_1\) ve \(\Psi_2\) için prefix tabloları kurulur ve daha büyük argümanlar önbelleğe alınır.

Daha sonra uygulama \(\lfloor N/\ell\rfloor\) değerinin sabit kaldığı blokları dolaşır. Her blokta doğru sayısı için ikinci derece formül değerlendirilir, üstel ve polinomsal toplamların kapalı formları uygulanır, tamamlayıcı sayı toplanır ve en sonda bu değer \(2^{(N+1)^2}\)'den \(10^8\) modunda çıkarılır.

Karmaşıklık Analizi

\(B\) totient ön hesaplama sınırı, \(Q\) ise \(\lfloor N/\ell\rfloor\)'nin farklı değer sayısı olsun; \(Q=O(\sqrt N)\) olur. Elek ve prefix tabloları yaklaşık doğrusal zamanda kurulup \(O(B)\) bellek kullanır. Dış toplam yalnızca \(O(Q)\) blok gezer ve büyük toplam sorguları bu bloklar arasında önbellekten tekrar kullanılır. Pratikte maliyetin ana kısmı tablo hazırlığı ile \(O(\sqrt N)\) ölçeğindeki blok aritmetiğidir.

Kaynaklar

  1. Problem sayfası: https://projecteuler.net/problem=415
  2. Sylvester-Gallai teoremi: Wikipedia — Sylvester-Gallai theorem
  3. Euler totient fonksiyonu: Wikipedia — Euler'in totient fonksiyonu
  4. Dirichlet hyperbola yöntemi: Wikipedia — Dirichlet hyperbola method
  5. Graham, Knuth, Patashnik, Concrete Mathematics, 2. baskı, Addison-Wesley, Bölüm 2.

Resumen del Problema

Sea \(G_N=\{(x,y)\in\mathbb{Z}^2:0\le x,y\le N\}\). Un conjunto finito \(S\subseteq G_N\) se llama titanic si existe una recta que pasa exactamente por dos puntos de \(S\). Debemos calcular \(T(N)\), el número de subconjuntos titanic de \(G_N\), módulo \(10^8\).

La implementación no prueba subconjuntos uno por uno. En cambio cuenta el complemento: el conjunto vacío, los singletons y los subconjuntos más grandes cuyos puntos elegidos son todos colineales.

Enfoque Matemático

Paso 1: Pasar al complemento

La herramienta conceptual es el teorema de Sylvester-Gallai: todo conjunto finito no colineal de puntos reales tiene una ordinary line, es decir, una recta que contiene exactamente dos de sus puntos. Por tanto, un subconjunto de la red no es titanic si y solo si es vacío, tiene un solo punto o todos sus puntos están en una misma recta.

Si \(\operatorname{NT}(N)\) denota el número de subconjuntos no titanic, entonces

$$T(N)=2^{(N+1)^2}-\operatorname{NT}(N)\pmod{10^8}.$$

Paso 2: Sustituir subconjuntos por longitudes de rectas

Supongamos que una recta de la red contiene exactamente \(k\) puntos. Los subconjuntos no titanic apoyados en esa recta son precisamente los de tamaño al menos \(3\), así que su contribución es

$$\sum_{r=3}^{k}\binom{k}{r}=2^k-1-k-\binom{k}{2}.$$

Definamos ahora \(F_\ell(N)\) como el número de rectas de la red que contienen al menos \(\ell+1\) puntos. Para una recta fija con \(k\) puntos se cumple

$$\sum_{\ell=2}^{k-1}\bigl(2^\ell-(\ell+1)\bigr)=2^k-1-k-\binom{k}{2}.$$

Por consiguiente,

$$\operatorname{NT}(N)=1+(N+1)^2+\sum_{\ell=2}^{N}F_\ell(N)\bigl(2^\ell-(\ell+1)\bigr).$$

El problema completo queda reducido a calcular \(F_\ell(N)\) de forma rápida para todos los \(\ell\ge2\).

Paso 3: Direcciones primitivas y colocación de segmentos

Escribamos \(a=N+1\). Toda recta no horizontal y no vertical de la red tiene un vector paso primitivo \((u,v)\) con \(1\le v\le u\) y \(\gcd(u,v)=1\). El par \((u,v)\) representa las familias de pendientes \(\pm v/u\) y, cuando \(u\ne v\), también las familias intercambiadas \(\pm u/v\). Las rectas horizontales y verticales se cuentan aparte.

Para una dirección primitiva así, el número de posiciones de un segmento de \(\ell\) pasos dentro del cuadrado es

$$C_\ell(u,v)=\max(a-\ell u,0)\max(a-\ell v,0).$$

Si una recta contiene \(k\) puntos de la red, aporta exactamente \(k-\ell\) segmentos de longitud \(\ell\) y \(k-\ell-1\) segmentos de longitud \(\ell+1\). La diferencia vale \(1\) exactamente cuando \(k\ge\ell+1\). Por ello, el número de rectas de esta clase direccional con al menos \(\ell+1\) puntos es

$$C_\ell(u,v)-C_{\ell+1}(u,v).$$

Al duplicar por los dos signos y sumar las \(a\) rectas horizontales y las \(a\) verticales, obtenemos \(F_\ell(N)\).

Paso 4: Estadísticas de dirección basadas en totientes

Definimos

$$\Phi(M)=\sum_{d\le M}\varphi(d),\qquad \Psi_1(M)=\sum_{d\le M} d\,\varphi(d),\qquad \Psi_2(M)=\sum_{d\le M} d^2\varphi(d).$$

La implementación condensa la información direccional en tres acumulados:

$$c(M)=2\Phi(M)-1,\qquad s_{xy}(M)=3\Psi_1(M)-1,\qquad s_{\prod}(M)=\Psi_2(M).$$

Estas fórmulas aparecen al agrupar las direcciones primitivas por su coordenada mayor \(d\). Para cada \(d>1\) hay \(\varphi(d)\) numeradores reducidos, su suma es \(d\varphi(d)/2\), y las direcciones intercambiadas aportan exactamente lo mismo. El caso diagonal \(d=1\) explica los términos constantes de corrección.

Paso 5: Evaluación rápida de \(\Phi\), \(\Psi_1\) y \(\Psi_2\)

Estas sumas no se recalculan desde cero para cada consulta. A partir de \(\sum_{d\mid n}\varphi(d)=n\) se obtienen las identidades

$$\sum_{q=1}^{n}\Phi\!\left(\left\lfloor\frac{n}{q}\right\rfloor\right)=\sum_{m=1}^{n}m=\frac{n(n+1)}{2},$$

$$\sum_{q=1}^{n}q\,\Psi_1\!\left(\left\lfloor\frac{n}{q}\right\rfloor\right)=\sum_{m=1}^{n}m^2=\frac{n(n+1)(2n+1)}{6},$$

$$\sum_{q=1}^{n}q^2\,\Psi_2\!\left(\left\lfloor\frac{n}{q}\right\rfloor\right)=\sum_{m=1}^{n}m^3=\left(\frac{n(n+1)}{2}\right)^2.$$

Eso genera recurrencias memoizadas sobre bloques de cocientes \(\left\lfloor n/q\right\rfloor\), que permiten consultar estas funciones muchas veces sin hacer un barrido lineal hasta \(n\).

Paso 6: Forma cerrada de \(F_\ell(N)\)

Tomemos

$$M=\left\lfloor\frac{N}{\ell}\right\rfloor,\qquad M'=\left\lfloor\frac{N}{\ell+1}\right\rfloor,\qquad a=N+1.$$

En cualquier intervalo de \(\ell\) donde \(M\) y \(M'\) permanecen constantes, el recuento de rectas es un polinomio cuadrático en \(\ell\):

$$F_\ell(N)=2a+2\left(q_2\ell^2+q_1\ell+q_0\right),$$

con

$$q_2=s_{\prod}(M)-s_{\prod}(M'),$$

$$q_1=a\bigl(s_{xy}(M')-s_{xy}(M)\bigr)-2s_{\prod}(M'),$$

$$q_0=a^2\bigl(c(M)-c(M')\bigr)+a\,s_{xy}(M')-s_{\prod}(M').$$

Esta es exactamente la fórmula por bloques que usa la implementación.

Paso 7: Suma por bloques

Los valores de \(\left\lfloor N/\ell\right\rfloor\) solo cambian \(O(\sqrt N)\) veces, así que la suma final se hace bloque por bloque. Para \(\ell\in[L_1,L_2]\), la contribución es

$$\sum_{\ell=L_1}^{L_2}F_\ell(N)\bigl(2^\ell-(\ell+1)\bigr).$$

Al sustituir el polinomio cuadrático, la implementación solo necesita formas cerradas para

$$\sum 2^\ell,\qquad \sum \ell 2^\ell,\qquad \sum \ell^2 2^\ell,\qquad \sum \ell,\qquad \sum \ell^2,\qquad \sum \ell^3.$$

Las identidades exponenciales empleadas son

$$\sum_{j=0}^{n}2^j=2^{n+1}-1,\qquad \sum_{j=0}^{n}j2^j=(n-1)2^{n+1}+2,$$

$$\sum_{j=0}^{n}j^2 2^j=(n^2-2n+3)2^{n+1}-6.$$

Ejemplo de verificación: \(N=4\)

Para la cuadrícula \(5\times5\), la fórmula da

$$F_2(4)=32,\qquad F_3(4)=16,\qquad F_4(4)=12.$$

Entonces

$$\operatorname{NT}(4)=1+25+32(1)+16(4)+12(11)=254,$$

y por tanto

$$T(4)=2^{25}-254=33554178,$$

que coincide con el valor de control usado por la implementación.

Cómo Funciona el Código

Las implementaciones en C++, Python y Java siguen la misma tubería matemática. Primero precalculan valores de la función totiente hasta un límite fijo mediante un cribado lineal, construyen tablas prefix para \(\Phi\), \(\Psi_1\) y \(\Psi_2\), y memorizan los argumentos grandes para no resolver la misma consulta dos veces.

Después, la implementación recorre los bloques distintos de \(\lfloor N/\ell\rfloor\). Dentro de cada bloque evalúa la fórmula cuadrática del número de rectas, aplica las formas cerradas de las sumas exponenciales y polinómicas, acumula el complemento y finalmente lo resta de \(2^{(N+1)^2}\) módulo \(10^8\).

Complejidad

Sea \(B\) el límite de precálculo del totiente y sea \(Q\) el número de valores distintos de \(\lfloor N/\ell\rfloor\); se tiene \(Q=O(\sqrt N)\). El cribado y las tablas prefix cuestan tiempo casi lineal y \(O(B)\) memoria. La suma exterior usa solo \(O(Q)\) bloques de cociente, y las consultas de sumas totiente se comparten entre bloques mediante caché. En la práctica, el coste dominante es la construcción de tablas junto con la aritmética por bloques de escala \(O(\sqrt N)\).

Referencias

  1. Página del problema: https://projecteuler.net/problem=415
  2. Teorema de Sylvester-Gallai: Wikipedia — Sylvester-Gallai theorem
  3. Función totiente de Euler: Wikipedia — Función φ de Euler
  4. Método de la hipérbola de Dirichlet: Wikipedia — Método de la hipérbola de Dirichlet
  5. Graham, Knuth, Patashnik, Concrete Mathematics, 2nd ed., Addison-Wesley, Chapter 2.

问题概述

设 \(G_N=\{(x,y)\in\mathbb{Z}^2:0\le x,y\le N\}\)。如果某个有限集合 \(S\subseteq G_N\) 存在一条直线恰好经过 \(S\) 中的两个点,那么 \(S\) 就称为 titanic 集。题目要求计算 \(G_N\) 的 titanic 子集个数 \(T(N)\),结果按 \(10^8\) 取模。

实现并不直接枚举子集,而是转而统计补集:空集、单点集,以及所有被选点完全共线的更大子集。

数学方法

步骤 1:先算补集

核心事实是 Sylvester-Gallai 定理:任意有限且不共线的实平面点集,都存在一条 ordinary line,也就是恰好只经过其中两个点的直线。因此,一个格点子集当且仅当它是空集、单点集,或者全部点共线时,才 不是 titanic 集。

记非 titanic 子集数为 \(\operatorname{NT}(N)\),则

$$T(N)=2^{(N+1)^2}-\operatorname{NT}(N)\pmod{10^8}.$$

步骤 2:把子集计数改写成直线长度计数

如果某条格点直线恰好包含 \(k\) 个格点,那么支撑在这条直线上的非 titanic 子集,正是大小至少为 \(3\) 的那些子集,因此它的贡献为

$$\sum_{r=3}^{k}\binom{k}{r}=2^k-1-k-\binom{k}{2}.$$

再定义 \(F_\ell(N)\) 为“至少包含 \(\ell+1\) 个格点的格点直线条数”。对一条固定的 \(k\) 点直线,有恒等式

$$\sum_{\ell=2}^{k-1}\bigl(2^\ell-(\ell+1)\bigr)=2^k-1-k-\binom{k}{2}.$$

因此

$$\operatorname{NT}(N)=1+(N+1)^2+\sum_{\ell=2}^{N}F_\ell(N)\bigl(2^\ell-(\ell+1)\bigr).$$

这样一来,问题就变成了如何快速求出所有 \(\ell\ge2\) 的 \(F_\ell(N)\)。

步骤 3:原始方向与线段摆放数

记 \(a=N+1\)。每一条既非水平也非竖直的格点直线,都对应一个原始步向量 \((u,v)\),其中 \(1\le v\le u\) 且 \(\gcd(u,v)=1\)。\((u,v)\) 代表斜率族 \(\pm v/u\);若 \(u\ne v\),还同时代表交换坐标后的 \(\pm u/v\)。水平线和竖直线单独处理。

对于这样的原始方向,边长方形中的 \(\ell\) 步线段摆放数为

$$C_\ell(u,v)=\max(a-\ell u,0)\max(a-\ell v,0).$$

如果某条直线含有 \(k\) 个格点,那么它恰好提供 \(k-\ell\) 条长度为 \(\ell\) 的线段,以及 \(k-\ell-1\) 条长度为 \(\ell+1\) 的线段。两者之差在且仅在 \(k\ge\ell+1\) 时等于 \(1\)。所以该方向类中“至少含 \(\ell+1\) 个格点”的直线条数就是

$$C_\ell(u,v)-C_{\ell+1}(u,v).$$

再对正负号乘以 \(2\),并加上 \(a\) 条水平线与 \(a\) 条竖直线,就得到 \(F_\ell(N)\)。

步骤 4:用欧拉函数汇总方向信息

定义

$$\Phi(M)=\sum_{d\le M}\varphi(d),\qquad \Psi_1(M)=\sum_{d\le M} d\,\varphi(d),\qquad \Psi_2(M)=\sum_{d\le M} d^2\varphi(d).$$

实现把原始方向的信息打包成三个累计量:

$$c(M)=2\Phi(M)-1,\qquad s_{xy}(M)=3\Psi_1(M)-1,\qquad s_{\prod}(M)=\Psi_2(M).$$

这些公式来自按较大坐标 \(d\) 分组。对固定 \(d>1\),既约分子有 \(\varphi(d)\) 个,它们的和是 \(d\varphi(d)/2\),而交换坐标的方向会再贡献同样的一份。\(d=1\) 的对角线情形则给出常数修正项。

步骤 5:快速求 \(\Phi\)、\(\Psi_1\)、\(\Psi_2\)

代码不会为每个查询重新累加这些和。由 \(\sum_{d\mid n}\varphi(d)=n\) 可以推出

$$\sum_{q=1}^{n}\Phi\!\left(\left\lfloor\frac{n}{q}\right\rfloor\right)=\sum_{m=1}^{n}m=\frac{n(n+1)}{2},$$

$$\sum_{q=1}^{n}q\,\Psi_1\!\left(\left\lfloor\frac{n}{q}\right\rfloor\right)=\sum_{m=1}^{n}m^2=\frac{n(n+1)(2n+1)}{6},$$

$$\sum_{q=1}^{n}q^2\,\Psi_2\!\left(\left\lfloor\frac{n}{q}\right\rfloor\right)=\sum_{m=1}^{n}m^3=\left(\frac{n(n+1)}{2}\right)^2.$$

于是可以按 \(\left\lfloor n/q\right\rfloor\) 的商分块写出记忆化递推,这就是实现中大量重复查询仍然高效的原因。

步骤 6:\(F_\ell(N)\) 的闭式

$$M=\left\lfloor\frac{N}{\ell}\right\rfloor,\qquad M'=\left\lfloor\frac{N}{\ell+1}\right\rfloor,\qquad a=N+1.$$

在任何一个 \(M\) 与 \(M'\) 都保持不变的 \(\ell\) 区间里,直线条数是 \(\ell\) 的二次多项式:

$$F_\ell(N)=2a+2\left(q_2\ell^2+q_1\ell+q_0\right),$$

其中

$$q_2=s_{\prod}(M)-s_{\prod}(M'),$$

$$q_1=a\bigl(s_{xy}(M')-s_{xy}(M)\bigr)-2s_{\prod}(M'),$$

$$q_0=a^2\bigl(c(M)-c(M')\bigr)+a\,s_{xy}(M')-s_{\prod}(M').$$

这正是实现中实际使用的分块二次公式。

步骤 7:按商分块求和

\(\left\lfloor N/\ell\right\rfloor\) 只会改变 \(O(\sqrt N)\) 次,因此最终求和按整段区间进行。对 \(\ell\in[L_1,L_2]\),贡献为

$$\sum_{\ell=L_1}^{L_2}F_\ell(N)\bigl(2^\ell-(\ell+1)\bigr).$$

代入二次式以后,只需要

$$\sum 2^\ell,\qquad \sum \ell 2^\ell,\qquad \sum \ell^2 2^\ell,\qquad \sum \ell,\qquad \sum \ell^2,\qquad \sum \ell^3$$

这些闭式。实现使用的指数求和恒等式是

$$\sum_{j=0}^{n}2^j=2^{n+1}-1,\qquad \sum_{j=0}^{n}j2^j=(n-1)2^{n+1}+2,$$

$$\sum_{j=0}^{n}j^2 2^j=(n^2-2n+3)2^{n+1}-6.$$

校验例子:\(N=4\)

对 \(5\times5\) 的格点方阵,上式给出

$$F_2(4)=32,\qquad F_3(4)=16,\qquad F_4(4)=12.$$

于是

$$\operatorname{NT}(4)=1+25+32(1)+16(4)+12(11)=254,$$

从而

$$T(4)=2^{25}-254=33554178,$$

与实现内置的校验值完全一致。

代码说明

C++、Python 和 Java 三个实现遵循同一套数学流程。它们先用线性筛预处理固定范围内的欧拉函数值,建立 \(\Phi\)、\(\Psi_1\)、\(\Psi_2\) 的前缀表,再把更大的参数结果缓存起来,避免重复求值。

之后,实现枚举 \(\lfloor N/\ell\rfloor\) 的所有不同商区间。在每个区间内,它计算二次型的直线条数,套用指数和多项式求和的闭式,累加非 titanic 集合个数,最后再从 \(2^{(N+1)^2}\) 中减去这个补集并对 \(10^8\) 取模。

复杂度分析

设 \(B\) 为欧拉函数预处理上界,\(Q\) 为 \(\lfloor N/\ell\rfloor\) 的不同取值个数,则 \(Q=O(\sqrt N)\)。筛法和前缀表构造需要近线性时间以及 \(O(B)\) 内存。外层求和只处理 \(O(Q)\) 个商分块,而大参数的 totient 求和通过缓存跨区间复用。实际运行中,主要开销来自前缀表构造和 \(O(\sqrt N)\) 量级的分块算术。

参考资料

  1. 题目页面: https://projecteuler.net/problem=415
  2. Sylvester-Gallai 定理: Wikipedia — Sylvester-Gallai theorem
  3. 欧拉函数: Wikipedia — 欧拉函数
  4. Dirichlet 双曲线法: Wikipedia — Dirichlet hyperbola method
  5. Graham, Knuth, Patashnik, Concrete Mathematics, 2nd ed., Addison-Wesley, Chapter 2.

Краткое описание

Пусть \(G_N=\{(x,y)\in\mathbb{Z}^2:0\le x,y\le N\}\). Конечное множество \(S\subseteq G_N\) называется titanic, если существует прямая, проходящая ровно через две точки из \(S\). Нужно вычислить \(T(N)\), то есть число titanic-подмножеств \(G_N\), по модулю \(10^8\).

Реализация не перебирает подмножества напрямую. Вместо этого она считает дополнение: пустое множество, одноточечные множества и более крупные наборы, все выбранные точки которых лежат на одной прямой.

Математический подход

Шаг 1: Переход к дополнению

Ключевой факт даёт теорема Сильвестра-Галлаи: любое конечное неколлинеарное множество точек на вещественной плоскости имеет ordinary line, то есть прямую, содержащую ровно две точки множества. Значит, решёточное подмножество не является titanic тогда и только тогда, когда оно пусто, состоит из одной точки или целиком коллинеарно.

Если \(\operatorname{NT}(N)\) обозначает число нетитанических множеств, то

$$T(N)=2^{(N+1)^2}-\operatorname{NT}(N)\pmod{10^8}.$$

Шаг 2: Замена подсчёта подмножеств подсчётом длин прямых

Пусть некоторая решёточная прямая содержит ровно \(k\) решёточных точек. Нетитанические подмножества на этой прямой — это в точности подмножества размера не меньше \(3\), поэтому вклад такой прямой равен

$$\sum_{r=3}^{k}\binom{k}{r}=2^k-1-k-\binom{k}{2}.$$

Обозначим через \(F_\ell(N)\) число решёточных прямых, содержащих не менее \(\ell+1\) точек. Для одной фиксированной прямой с \(k\) точками выполняется тождество

$$\sum_{\ell=2}^{k-1}\bigl(2^\ell-(\ell+1)\bigr)=2^k-1-k-\binom{k}{2}.$$

Следовательно,

$$\operatorname{NT}(N)=1+(N+1)^2+\sum_{\ell=2}^{N}F_\ell(N)\bigl(2^\ell-(\ell+1)\bigr).$$

Тем самым задача сводится к быстрому вычислению \(F_\ell(N)\) для всех \(\ell\ge2\).

Шаг 3: Примитивные направления и размещения отрезков

Положим \(a=N+1\). Каждая решёточная прямая, не являющаяся горизонтальной или вертикальной, имеет примитивный шаговый вектор \((u,v)\) с \(1\le v\le u\) и \(\gcd(u,v)=1\). Пара \((u,v)\) представляет семейства наклонов \(\pm v/u\), а при \(u\ne v\) ещё и переставленные семейства \(\pm u/v\). Горизонтальные и вертикальные прямые учитываются отдельно.

Для такого примитивного направления число размещений \(\ell\)-шагового отрезка внутри квадрата равно

$$C_\ell(u,v)=\max(a-\ell u,0)\max(a-\ell v,0).$$

Если прямая содержит \(k\) решёточных точек, то она даёт ровно \(k-\ell\) отрезков длины \(\ell\) и \(k-\ell-1\) отрезков длины \(\ell+1\). Разность равна \(1\) тогда и только тогда, когда \(k\ge\ell+1\). Значит, число прямых этого класса направлений, содержащих как минимум \(\ell+1\) точек, равно

$$C_\ell(u,v)-C_{\ell+1}(u,v).$$

После умножения на \(2\) для двух знаков и добавления \(a\) горизонтальных и \(a\) вертикальных прямых получается \(F_\ell(N)\).

Шаг 4: Суммы по направлениям через функцию Эйлера

Определим

$$\Phi(M)=\sum_{d\le M}\varphi(d),\qquad \Psi_1(M)=\sum_{d\le M} d\,\varphi(d),\qquad \Psi_2(M)=\sum_{d\le M} d^2\varphi(d).$$

В реализации информация о примитивных направлениях упакована в три накопительные функции:

$$c(M)=2\Phi(M)-1,\qquad s_{xy}(M)=3\Psi_1(M)-1,\qquad s_{\prod}(M)=\Psi_2(M).$$

Эти формулы получаются при группировке направлений по большей координате \(d\). Для фиксированного \(d>1\) существует \(\varphi(d)\) несократимых числителей, их сумма равна \(d\varphi(d)/2\), а переставленные направления дают симметричный вклад. Диагональный случай \(d=1\) и порождает постоянные поправки.

Шаг 5: Быстрое вычисление \(\Phi\), \(\Psi_1\), \(\Psi_2\)

Эти суммы не пересчитываются с нуля для каждого аргумента. Из тождества \(\sum_{d\mid n}\varphi(d)=n\) выводятся соотношения

$$\sum_{q=1}^{n}\Phi\!\left(\left\lfloor\frac{n}{q}\right\rfloor\right)=\sum_{m=1}^{n}m=\frac{n(n+1)}{2},$$

$$\sum_{q=1}^{n}q\,\Psi_1\!\left(\left\lfloor\frac{n}{q}\right\rfloor\right)=\sum_{m=1}^{n}m^2=\frac{n(n+1)(2n+1)}{6},$$

$$\sum_{q=1}^{n}q^2\,\Psi_2\!\left(\left\lfloor\frac{n}{q}\right\rfloor\right)=\sum_{m=1}^{n}m^3=\left(\frac{n(n+1)}{2}\right)^2.$$

Это даёт мемоизированные рекурсии по блокам одинаковых значений \(\left\lfloor n/q\right\rfloor\), поэтому большие аргументы можно многократно запрашивать без линейного прохода до \(n\).

Шаг 6: Замкнутая формула для \(F_\ell(N)\)

Положим

$$M=\left\lfloor\frac{N}{\ell}\right\rfloor,\qquad M'=\left\lfloor\frac{N}{\ell+1}\right\rfloor,\qquad a=N+1.$$

На любом интервале \(\ell\), где \(M\) и \(M'\) постоянны, число прямых является квадратичным полиномом по \(\ell\):

$$F_\ell(N)=2a+2\left(q_2\ell^2+q_1\ell+q_0\right),$$

где

$$q_2=s_{\prod}(M)-s_{\prod}(M'),$$

$$q_1=a\bigl(s_{xy}(M')-s_{xy}(M)\bigr)-2s_{\prod}(M'),$$

$$q_0=a^2\bigl(c(M)-c(M')\bigr)+a\,s_{xy}(M')-s_{\prod}(M').$$

Именно этот квадратичный блочный вид использует программа.

Шаг 7: Суммирование по блокам

Значения \(\left\lfloor N/\ell\right\rfloor\) меняются лишь \(O(\sqrt N)\) раз, поэтому конечная сумма считается блоками. Для \(\ell\in[L_1,L_2]\) вклад равен

$$\sum_{\ell=L_1}^{L_2}F_\ell(N)\bigl(2^\ell-(\ell+1)\bigr).$$

После подстановки квадратичной формы остаются только замкнутые формулы для

$$\sum 2^\ell,\qquad \sum \ell 2^\ell,\qquad \sum \ell^2 2^\ell,\qquad \sum \ell,\qquad \sum \ell^2,\qquad \sum \ell^3.$$

Используемые экспоненциальные тождества таковы:

$$\sum_{j=0}^{n}2^j=2^{n+1}-1,\qquad \sum_{j=0}^{n}j2^j=(n-1)2^{n+1}+2,$$

$$\sum_{j=0}^{n}j^2 2^j=(n^2-2n+3)2^{n+1}-6.$$

Контрольный пример: \(N=4\)

Для решётки \(5\times5\) формула даёт

$$F_2(4)=32,\qquad F_3(4)=16,\qquad F_4(4)=12.$$

Значит,

$$\operatorname{NT}(4)=1+25+32(1)+16(4)+12(11)=254,$$

и потому

$$T(4)=2^{25}-254=33554178,$$

что точно совпадает с контрольным значением реализации.

Как работает код

Реализации на C++, Python и Java следуют одной и той же математической схеме. Сначала они вычисляют значения функции Эйлера до фиксированной границы линейным решетом, строят префиксные таблицы для \(\Phi\), \(\Psi_1\) и \(\Psi_2\), а затем кешируют результаты для больших аргументов.

После этого реализация перебирает различные блоки значений \(\lfloor N/\ell\rfloor\). Внутри каждого блока вычисляется квадратичная формула для числа прямых, применяются замкнутые формулы для экспоненциальных и полиномиальных сумм, накапливается дополнение и в самом конце вычитается из \(2^{(N+1)^2}\) по модулю \(10^8\).

Анализ сложности

Пусть \(B\) — граница предварительного вычисления totient, а \(Q\) — число различных значений \(\lfloor N/\ell\rfloor\); тогда \(Q=O(\sqrt N)\). Решето и префиксные таблицы требуют почти линейного времени и \(O(B)\) памяти. Внешняя сумма использует лишь \(O(Q)\) блоков, а большие сумматорные запросы totient переиспользуются между блоками через кеш. На практике основную стоимость дают построение таблиц и блочная арифметика масштаба \(O(\sqrt N)\).

Источники

  1. Страница задачи: https://projecteuler.net/problem=415
  2. Теорема Сильвестра-Галлаи: Wikipedia — Теорема Сильвестра — Галлаи
  3. Функция Эйлера: Wikipedia — Функция Эйлера
  4. Метод гиперболы Дирихле: Wikipedia — Метод гиперболы Дирихле
  5. Graham, Knuth, Patashnik, Concrete Mathematics, 2nd ed., Addison-Wesley, Chapter 2.

ملخص المسألة

لتكن \(G_N=\{(x,y)\in\mathbb{Z}^2:0\le x,y\le N\}\). نسمّي المجموعة المنتهية \(S\subseteq G_N\) مجموعة titanic إذا وُجد مستقيم يمرّ عبر نقطتين فقط من نقاط \(S\). المطلوب هو حساب \(T(N)\)، أي عدد المجموعات الجزئية titanic من \(G_N\)، بترديد \(10^8\).

التنفيذ لا يجرّب جميع المجموعات الجزئية مباشرة. بدلاً من ذلك يحسب المتمم: المجموعة الخالية، والمجموعات ذات النقطة الواحدة، والمجموعات الأكبر التي تقع كل نقاطها المختارة على مستقيم واحد.

المنهج الرياضي

الخطوة 1: الانتقال إلى المتمم

الحقيقة الأساسية هنا هي مبرهنة Sylvester-Gallai: كل مجموعة منتهية غير مستقيمية في المستوى الحقيقي لها ordinary line، أي مستقيم يحتوي نقطتين فقط من المجموعة. لذلك فإن مجموعة شبكية لا تكون titanic إلا إذا كانت خالية، أو تحوي نقطة واحدة فقط، أو كانت جميع نقاطها على استقامة واحدة.

إذا رمزنا بعدد المجموعات غير titanic بالرمز \(\operatorname{NT}(N)\)، فإن

$$T(N)=2^{(N+1)^2}-\operatorname{NT}(N)\pmod{10^8}.$$

الخطوة 2: تحويل العد إلى أطوال المستقيمات

إذا كان مستقيم شبكي ما يحتوي بالضبط على \(k\) نقاط شبكية، فإن المجموعات غير titanic المدعومة على هذا المستقيم هي بالضبط المجموعات التي حجمها لا يقل عن \(3\)، ولذلك يكون مساهمته

$$\sum_{r=3}^{k}\binom{k}{r}=2^k-1-k-\binom{k}{2}.$$

عرّف الآن \(F_\ell(N)\) بأنه عدد المستقيمات الشبكية التي تحتوي على \(\ell+1\) نقطة أو أكثر. ولأي مستقيم ثابت يحوي \(k\) نقاط لدينا الهوية

$$\sum_{\ell=2}^{k-1}\bigl(2^\ell-(\ell+1)\bigr)=2^k-1-k-\binom{k}{2}.$$

ومن ثم

$$\operatorname{NT}(N)=1+(N+1)^2+\sum_{\ell=2}^{N}F_\ell(N)\bigl(2^\ell-(\ell+1)\bigr).$$

وهكذا تصبح المسألة كلها هي حساب \(F_\ell(N)\) بسرعة لكل \(\ell\ge2\).

الخطوة 3: الاتجاهات الأولية وعدد تموضعات القطع

لنكتب \(a=N+1\). كل مستقيم شبكي غير أفقي وغير عمودي يملك متجه خطوة أولي \((u,v)\) حيث \(1\le v\le u\) و \(\gcd(u,v)=1\). الزوج \((u,v)\) يمثّل عائلتي الميل \(\pm v/u\)، وإذا كان \(u\ne v\) فإنه يمثّل أيضًا العائلتين المتبادلتين \(\pm u/v\). أما المستقيمات الأفقية والعمودية فتُحسب على حدة.

لذلك الاتجاه الأولي يكون عدد تموضعات قطعة طولها \(\ell\) داخل المربع هو

$$C_\ell(u,v)=\max(a-\ell u,0)\max(a-\ell v,0).$$

إذا احتوى مستقيم ما على \(k\) نقاط شبكية، فإنه يعطي بالضبط \(k-\ell\) قطعًا بطول \(\ell\)، ويعطي \(k-\ell-1\) قطعًا بطول \(\ell+1\). الفرق بينهما يساوي \(1\) بالضبط عندما يكون \(k\ge\ell+1\). لذلك فإن عدد المستقيمات في هذه الفئة الاتجاهية التي تحتوي على \(\ell+1\) نقطة أو أكثر هو

$$C_\ell(u,v)-C_{\ell+1}(u,v).$$

بعد الضرب في \(2\) من أجل الإشارتين وإضافة \(a\) مستقيمًا أفقيًا و\(a\) مستقيمًا عموديًا نحصل على \(F_\ell(N)\).

الخطوة 4: إحصاءات الاتجاه المعتمدة على دالة أويلر

نعرّف

$$\Phi(M)=\sum_{d\le M}\varphi(d),\qquad \Psi_1(M)=\sum_{d\le M} d\,\varphi(d),\qquad \Psi_2(M)=\sum_{d\le M} d^2\varphi(d).$$

ويجمع التنفيذ معلومات الاتجاهات الأولية في ثلاث كميات تراكمية:

$$c(M)=2\Phi(M)-1,\qquad s_{xy}(M)=3\Psi_1(M)-1,\qquad s_{\prod}(M)=\Psi_2(M).$$

تظهر هذه الصيغ عند تجميع الاتجاهات بحسب الإحداثي الأكبر \(d\). لكل \(d>1\) يوجد \(\varphi(d)\) بسطًا مختزلاً، ومجموع هذه البسوط يساوي \(d\varphi(d)/2\)، كما أن الاتجاهات الناتجة عن تبديل الإحداثيين تعطي الإسهام نفسه. أما الحالة القطرية \(d=1\) فهي التي تولّد حدود التصحيح الثابتة.

الخطوة 5: الحساب السريع لـ \(\Phi\) و \(\Psi_1\) و \(\Psi_2\)

هذه المجاميع لا تُعاد من الصفر لكل استعلام. من الهوية \(\sum_{d\mid n}\varphi(d)=n\) نحصل على

$$\sum_{q=1}^{n}\Phi\!\left(\left\lfloor\frac{n}{q}\right\rfloor\right)=\sum_{m=1}^{n}m=\frac{n(n+1)}{2},$$

$$\sum_{q=1}^{n}q\,\Psi_1\!\left(\left\lfloor\frac{n}{q}\right\rfloor\right)=\sum_{m=1}^{n}m^2=\frac{n(n+1)(2n+1)}{6},$$

$$\sum_{q=1}^{n}q^2\,\Psi_2\!\left(\left\lfloor\frac{n}{q}\right\rfloor\right)=\sum_{m=1}^{n}m^3=\left(\frac{n(n+1)}{2}\right)^2.$$

وهذا يقود إلى علاقات تراجعية محفوظة في الذاكرة على كتل القيم المتساوية لـ \(\left\lfloor n/q\right\rfloor\)، ولذلك يمكن استدعاء هذه الدوال كثيرًا من دون مسح خطي حتى \(n\).

الخطوة 6: الصيغة المغلقة لـ \(F_\ell(N)\)

لنضع

$$M=\left\lfloor\frac{N}{\ell}\right\rfloor,\qquad M'=\left\lfloor\frac{N}{\ell+1}\right\rfloor,\qquad a=N+1.$$

على أي مجال تبقى فيه \(M\) و\(M'\) ثابتتين، يصبح عدد المستقيمات كثير حدود تربيعيًا في \(\ell\):

$$F_\ell(N)=2a+2\left(q_2\ell^2+q_1\ell+q_0\right),$$

حيث

$$q_2=s_{\prod}(M)-s_{\prod}(M'),$$

$$q_1=a\bigl(s_{xy}(M')-s_{xy}(M)\bigr)-2s_{\prod}(M'),$$

$$q_0=a^2\bigl(c(M)-c(M')\bigr)+a\,s_{xy}(M')-s_{\prod}(M').$$

وهذه هي بالضبط الصيغة التربيعية الكتلية التي يستخدمها التنفيذ.

الخطوة 7: الجمع على شكل كتل

القيم \(\left\lfloor N/\ell\right\rfloor\) لا تتغير إلا \(O(\sqrt N)\) مرة، لذلك يُنفَّذ الجمع النهائي على شكل كتل. إذا كان \(\ell\in[L_1,L_2]\)، فإن المساهمة تساوي

$$\sum_{\ell=L_1}^{L_2}F_\ell(N)\bigl(2^\ell-(\ell+1)\bigr).$$

بعد التعويض عن كثير الحدود التربيعي، لا يحتاج التنفيذ إلا إلى صيغ مغلقة لـ

$$\sum 2^\ell,\qquad \sum \ell 2^\ell,\qquad \sum \ell^2 2^\ell,\qquad \sum \ell,\qquad \sum \ell^2,\qquad \sum \ell^3.$$

ومن الهويات الأسية المستخدمة

$$\sum_{j=0}^{n}2^j=2^{n+1}-1,\qquad \sum_{j=0}^{n}j2^j=(n-1)2^{n+1}+2,$$

$$\sum_{j=0}^{n}j^2 2^j=(n^2-2n+3)2^{n+1}-6.$$

مثال تحقق: \(N=4\)

في الشبكة \(5\times5\) تعطي الصيغة

$$F_2(4)=32,\qquad F_3(4)=16,\qquad F_4(4)=12.$$

ومن ثم

$$\operatorname{NT}(4)=1+25+32(1)+16(4)+12(11)=254,$$

وبالتالي

$$T(4)=2^{25}-254=33554178,$$

وهو تمامًا مقدار التحقق الذي تستخدمه الخوارزمية.

كيف يعمل الكود

تتبع تطبيقات C++ وPython وJava المسار الرياضي نفسه. فهي تحسب أولًا قيم دالة أويلر حتى حد ثابت باستعمال غربال خطي، ثم تبني جداول prefix لـ \(\Phi\) و\(\Psi_1\) و\(\Psi_2\)، وبعد ذلك تخزّن نتائج الوسائط الكبيرة كي لا يُعاد الحساب نفسه.

بعد ذلك يستعرض التنفيذ كتل القيم المختلفة لـ \(\lfloor N/\ell\rfloor\). داخل كل كتلة يقيّم الصيغة التربيعية لعدد المستقيمات، ويطبّق الصيغ المغلقة لمجاميع الحدود الأسية وكثيرات الحدود، ويجمع عدد المجموعات غير titanic، ثم يطرحه أخيرًا من \(2^{(N+1)^2}\) بترديد \(10^8\).

تحليل التعقيد

ليكن \(B\) حدّ التحضير المسبق لقيم totient، وليكن \(Q\) عدد القيم المختلفة لـ \(\lfloor N/\ell\rfloor\)، وعندئذٍ \(Q=O(\sqrt N)\). يحتاج الغربال وجداول prefix إلى زمن شبه خطي وذاكرة \(O(B)\). أما الجمع الخارجي فلا يمر إلا على \(O(Q)\) من الكتل، كما أن استعلامات المجاميع الكبيرة يعاد استخدامها بين هذه الكتل بفضل التخزين المؤقت. عمليًا تهيمن كلفة بناء الجداول مع الحساب الكتلي على مقياس \(O(\sqrt N)\).

مراجع

  1. صفحة المسألة: https://projecteuler.net/problem=415
  2. مبرهنة Sylvester-Gallai: Wikipedia — Sylvester-Gallai theorem
  3. دالة أويلر: Wikipedia — دالة أويلر
  4. طريقة Dirichlet hyperbola: Wikipedia — Dirichlet hyperbola method
  5. Graham, Knuth, Patashnik, Concrete Mathematics, 2nd ed., Addison-Wesley, Chapter 2.