Problem Summary

For each \(n\), the counting formula used by the implementations is

$$C(n)=6\sum_{k=0}^{\pi(n)} \binom{n-1}{k}5^{\,n-1-k},$$

where \(\pi(n)\) is the number of primes not exceeding \(n\). The final target is the cumulative sum

$$S(L)=\sum_{n=1}^{L} C(n)\pmod{10^9+7},\qquad L=50{,}000{,}000.$$

A direct evaluation of every truncated binomial sum would be far too slow, so the solution derives a constant-time transition from \(n\) to \(n+1\).

Mathematical Approach

Step 1: Isolate the Summand

Define

$$B_n(k)=\binom{n-1}{k}5^{\,n-1-k},\qquad t_n=\pi(n).$$

Then the inner truncated sum is

$$P_n=\sum_{k=0}^{t_n} B_n(k),$$

so the quantity contributed at level \(n\) is simply

$$C(n)=6P_n.$$

The whole problem is therefore reduced to updating \(P_n\) efficiently as \(n\) increases.

Step 2: Pascal's Identity Gives the Row Transition

Using

$$\binom{n}{k}=\binom{n-1}{k}+\binom{n-1}{k-1},$$

we obtain

$$B_{n+1}(k)=\binom{n}{k}5^{\,n-k}=5B_n(k)+B_n(k-1).$$

This recurrence is the key structural fact. It says that every term in the next row depends only on two neighboring terms from the current row, so there is no need to rebuild the full truncated sum from scratch.

Step 3: Only the Boundary Matters

The cutoff \(t_n=\pi(n)\) changes by at most one when \(n\) increases. Therefore it is enough to keep:

$$P_n=\sum_{k=0}^{t_n} B_n(k),\qquad U_n=B_n(t_n),\qquad V_n=B_n(t_n+1).$$

These are, respectively, the truncated sum, the last included term, and the first excluded term. The initial state is immediate from \(n=1\):

$$t_1=\pi(1)=0,\qquad P_1=1,\qquad U_1=1,\qquad V_1=0.$$

Step 4: Transition When \(n+1\) Is Composite

If \(n+1\) is composite, then \(t_{n+1}=t_n=t\). Summing the row transition up to \(k=t\) gives

$$\begin{aligned} P_{n+1} &=\sum_{k=0}^{t}\left(5B_n(k)+B_n(k-1)\right) \\ &=5\sum_{k=0}^{t}B_n(k)+\sum_{k=0}^{t-1}B_n(k) \\ &=6P_n-U_n. \end{aligned}$$

The new boundary terms are

$$U_{n+1}=5U_n+B_n(t-1),\qquad V_{n+1}=5V_n+U_n.$$

The missing term \(B_n(t-1)\) is recovered from a ratio:

$$\frac{B_n(t-1)}{B_n(t)}=\frac{\binom{n-1}{t-1}5^{\,n-t}}{\binom{n-1}{t}5^{\,n-1-t}}=\frac{5t}{n-t},$$

hence

$$B_n(t-1)=U_n\cdot \frac{5t}{n-t}.$$

Step 5: Transition When \(n+1\) Is Prime

If \(n+1\) is prime, then the cutoff moves to the right: \(t_{n+1}=t_n+1=t+1\). The next truncated sum now includes one extra boundary term:

$$P_{n+1}=6P_n+5V_n.$$

The new last included term is

$$U_{n+1}=5V_n+U_n,$$

and the new first excluded term satisfies

$$V_{n+1}=5B_n(t+2)+V_n.$$

Again a ratio gives the unseen term:

$$\frac{B_n(t+2)}{B_n(t+1)}=\frac{\binom{n-1}{t+2}5^{\,n-t-3}}{\binom{n-1}{t+1}5^{\,n-t-2}}=\frac{n-t-2}{5(t+2)},$$

so

$$B_n(t+2)=V_n\cdot \frac{n-t-2}{5(t+2)}.$$

If \(t+2>n-1\), that term is simply \(0\), exactly as the implementations handle it.

Step 6: Modular Arithmetic and Final Accumulation

Every step contributes

$$C(n)=6P_n,$$

therefore

$$S(L)\equiv \sum_{n=1}^{L} 6P_n \pmod{10^9+7}.$$

The recurrence contains divisions by \(n-t\), \(t+2\), and \(5\). Since the modulus \(10^9+7\) is prime and all these numbers are positive and strictly smaller than the modulus for the target range, each division is replaced by multiplication with a modular inverse.

Sanity Checks

The derived formulas reproduce the exact checkpoint values used by the implementations:

$$C(3)=216,\qquad C(4)=1290,\qquad C(11)=361912500,$$

$$C(24)=4727547363281250000,$$

and for the cumulative sum,

$$S(50)\equiv 832833871 \pmod{10^9+7}.$$

How the Code Works

The C++, Python, and Java implementations all follow the same plan. They first build a primality table up to \(L+1\), because only the question “is \(n+1\) prime?” decides which recurrence branch to use. They also precompute modular inverses up to \(L+2\), which turns the rational boundary ratios into constant-time modular multiplications. A single forward pass then maintains the truncated sum together with the two boundary terms, updates them with the prime or composite formulas above, and adds \(6P_n\) to the running answer at each step.

Complexity Analysis

The sieve costs \(O(L\log\log L)\) time. Precomputing modular inverses costs \(O(L)\) time. The recurrence itself performs only constant-time arithmetic per \(n\), so the main loop is \(O(L)\). Overall the method is \(O(L\log\log L)\) time and \(O(L)\) memory, with only \(O(1)\) recurrence state beyond the primality and inverse tables.

Footnotes and References

  1. Problem page: https://projecteuler.net/problem=423
  2. Binomial coefficient and Pascal's identity: Wikipedia — Binomial coefficient
  3. Prime-counting function \(\pi(n)\): Wikipedia — Prime-counting function
  4. Sieve of Eratosthenes: Wikipedia — Sieve of Eratosthenes
  5. Modular multiplicative inverse: Wikipedia — Modular multiplicative inverse

Problemzusammenfassung

Für jedes \(n\) verwenden die Implementierungen die exakte Zählformel

$$C(n)=6\sum_{k=0}^{\pi(n)} \binom{n-1}{k}5^{\,n-1-k},$$

wobei \(\pi(n)\) die Anzahl der Primzahlen \(\le n\) ist. Gesucht ist die kumulative Summe

$$S(L)=\sum_{n=1}^{L} C(n)\pmod{10^9+7},\qquad L=50{,}000{,}000.$$

Eine direkte Auswertung jeder abgeschnittenen Binomialsumme wäre viel zu langsam. Deshalb leitet die Lösung einen Übergang in \(O(1)\) von \(n\) nach \(n+1\) her.

Mathematischer Ansatz

Schritt 1: Den Summanden isolieren

Wir definieren

$$B_n(k)=\binom{n-1}{k}5^{\,n-1-k},\qquad t_n=\pi(n).$$

Dann ist die abgeschnittene Summe

$$P_n=\sum_{k=0}^{t_n} B_n(k),$$

und der Beitrag auf Stufe \(n\) lautet einfach

$$C(n)=6P_n.$$

Damit reduziert sich das Problem darauf, \(P_n\) effizient fortzuschreiben.

Schritt 2: Pascalsche Identität liefert den Zeilenübergang

Aus

$$\binom{n}{k}=\binom{n-1}{k}+\binom{n-1}{k-1}$$

folgt sofort

$$B_{n+1}(k)=\binom{n}{k}5^{\,n-k}=5B_n(k)+B_n(k-1).$$

Das ist die zentrale Rekursion. Jede Größe der nächsten Zeile hängt nur von zwei benachbarten Größen der aktuellen Zeile ab.

Schritt 3: Nur die Grenze ist wichtig

Der Grenzwert \(t_n=\pi(n)\) ändert sich beim Übergang von \(n\) zu \(n+1\) um höchstens eins. Deshalb genügt es, drei Größen zu speichern:

$$P_n=\sum_{k=0}^{t_n} B_n(k),\qquad U_n=B_n(t_n),\qquad V_n=B_n(t_n+1).$$

Das sind die abgeschnittene Summe, der letzte einbezogene Term und der erste ausgeschlossene Term. Für \(n=1\) erhält man direkt

$$t_1=\pi(1)=0,\qquad P_1=1,\qquad U_1=1,\qquad V_1=0.$$

Schritt 4: Übergang, wenn \(n+1\) zusammengesetzt ist

Ist \(n+1\) nicht prim, dann bleibt \(t_{n+1}=t_n=t\). Summation des Zeilenübergangs bis \(k=t\) ergibt

$$\begin{aligned} P_{n+1} &=\sum_{k=0}^{t}\left(5B_n(k)+B_n(k-1)\right) \\ &=5\sum_{k=0}^{t}B_n(k)+\sum_{k=0}^{t-1}B_n(k) \\ &=6P_n-U_n. \end{aligned}$$

Für die neuen Randterme gilt

$$U_{n+1}=5U_n+B_n(t-1),\qquad V_{n+1}=5V_n+U_n.$$

Der fehlende Term wird über ein Verhältnis gewonnen:

$$\frac{B_n(t-1)}{B_n(t)}=\frac{\binom{n-1}{t-1}5^{\,n-t}}{\binom{n-1}{t}5^{\,n-1-t}}=\frac{5t}{n-t},$$

also

$$B_n(t-1)=U_n\cdot \frac{5t}{n-t}.$$

Schritt 5: Übergang, wenn \(n+1\) prim ist

Ist \(n+1\) prim, dann verschiebt sich die Grenze nach rechts: \(t_{n+1}=t_n+1=t+1\). Dadurch kommt ein zusätzlicher Randterm in die Summe:

$$P_{n+1}=6P_n+5V_n.$$

Der neue letzte einbezogene Term ist

$$U_{n+1}=5V_n+U_n,$$

und für den neuen ersten ausgeschlossenen Term gilt

$$V_{n+1}=5B_n(t+2)+V_n.$$

Wieder hilft ein Quotient:

$$\frac{B_n(t+2)}{B_n(t+1)}=\frac{\binom{n-1}{t+2}5^{\,n-t-3}}{\binom{n-1}{t+1}5^{\,n-t-2}}=\frac{n-t-2}{5(t+2)},$$

daher

$$B_n(t+2)=V_n\cdot \frac{n-t-2}{5(t+2)}.$$

Falls \(t+2>n-1\), ist dieser Term gleich \(0\), genau wie in den Implementierungen.

Schritt 6: Modulare Arithmetik und Endsumme

Jeder Schritt trägt

$$C(n)=6P_n$$

bei, also

$$S(L)\equiv \sum_{n=1}^{L} 6P_n \pmod{10^9+7}.$$

In der Rekursion treten Divisionen durch \(n-t\), \(t+2\) und \(5\) auf. Da der Modul \(10^9+7\) prim ist und alle diese Werte im Zielbereich positiv und kleiner als der Modul sind, lassen sich alle Divisionen durch multiplikative Inverse modulo \(10^9+7\) ersetzen.

Plausibilitätsprüfungen

Die hergeleiteten Formeln reproduzieren die exakten Kontrollwerte aus den Implementierungen:

$$C(3)=216,\qquad C(4)=1290,\qquad C(11)=361912500,$$

$$C(24)=4727547363281250000,$$

sowie für die kumulative Summe

$$S(50)\equiv 832833871 \pmod{10^9+7}.$$

Wie der Code arbeitet

Die C++-, Python- und Java-Implementierungen folgen demselben Aufbau. Zuerst wird eine Primtabelle bis \(L+1\) erzeugt, denn nur die Frage „ist \(n+1\) prim?“ entscheidet über den benutzten Rekursionszweig. Danach werden modulare Inverse bis \(L+2\) vorab berechnet, sodass die rationalen Randverhältnisse als konstante modulare Multiplikationen ausgeführt werden können. Anschließend läuft genau ein Vorwärtsdurchgang über alle \(n\), hält die abgeschnittene Summe und die beiden Randterme aktuell, aktualisiert sie mit den Prim-/Nichtprim-Formeln und addiert in jedem Schritt \(6P_n\) zum Ergebnis.

Komplexitätsanalyse

Das Primzahlsieb benötigt \(O(L\log\log L)\) Zeit. Die Vorberechnung der modularen Inversen kostet \(O(L)\) Zeit. Die eigentliche Rekursion führt pro \(n\) nur konstant viele modulare Operationen aus und ist daher \(O(L)\). Insgesamt ergibt sich \(O(L\log\log L)\) Zeit und \(O(L)\) Speicher, wobei der rekursive Zustand selbst nur \(O(1)\) Platz belegt.

Fußnoten und Quellen

  1. Problemseite: https://projecteuler.net/problem=423
  2. Binomialkoeffizient und Pascalsche Identität: Wikipedia — Binomialkoeffizient
  3. Primzahlzählfunktion \(\pi(n)\): Wikipedia — Primzahlzählfunktion
  4. Sieb des Eratosthenes: Wikipedia — Sieb des Eratosthenes
  5. Modulares multiplikatives Inverses: Wikipedia — Multiplikative Inverse

Problem Özeti

Her \(n\) için uygulamaların kullandığı tam sayım formülü

$$C(n)=6\sum_{k=0}^{\pi(n)} \binom{n-1}{k}5^{\,n-1-k}$$

şeklindedir; burada \(\pi(n)\), \(n\)'den büyük olmayan asal sayıların adedidir. Amaç, kümülatif toplamı

$$S(L)=\sum_{n=1}^{L} C(n)\pmod{10^9+7},\qquad L=50{,}000{,}000$$

olarak hesaplamaktır. Her bir kesilmiş binom toplamını baştan hesaplamak çok pahalı olacağı için çözüm, \(n\)'den \(n+1\)'e sabit zamanlı bir geçiş çıkarır.

Matematiksel Yaklaşım

Adım 1: Toplamın Tek Terimini Ayırmak

Şöyle tanımlayalım:

$$B_n(k)=\binom{n-1}{k}5^{\,n-1-k},\qquad t_n=\pi(n).$$

Buna göre kesilmiş iç toplam

$$P_n=\sum_{k=0}^{t_n} B_n(k)$$

olur ve \(n\) seviyesindeki katkı doğrudan

$$C(n)=6P_n$$

şeklinde yazılır. Dolayısıyla asıl iş, \(P_n\) değerini hızlı güncellemektir.

Adım 2: Pascal Özdeşliği Satır Geçişini Verir

$$\binom{n}{k}=\binom{n-1}{k}+\binom{n-1}{k-1}$$

özdeşliğini kullanınca

$$B_{n+1}(k)=\binom{n}{k}5^{\,n-k}=5B_n(k)+B_n(k-1)$$

elde edilir. Bu temel rekürsiyon sayesinde bir sonraki satırın her terimi, mevcut satırdaki iki komşu terimden gelir; tüm satırı yeniden kurmaya gerek kalmaz.

Adım 3: Sadece Sınır Bilgisi Yeterlidir

Kesme noktası \(t_n=\pi(n)\), \(n\) bir arttığında en fazla bir artar. Bu yüzden şu üç değeri taşımak yeterlidir:

$$P_n=\sum_{k=0}^{t_n} B_n(k),\qquad U_n=B_n(t_n),\qquad V_n=B_n(t_n+1).$$

Bunlar sırasıyla kesilmiş toplam, dahil edilen son terim ve dışarıda kalan ilk terimdir. Başlangıç durumu \(n=1\) için açıktır:

$$t_1=\pi(1)=0,\qquad P_1=1,\qquad U_1=1,\qquad V_1=0.$$

Adım 4: \(n+1\) Asal Değilse Geçiş

Eğer \(n+1\) bileşikse \(t_{n+1}=t_n=t\) kalır. Satır geçişini \(k=t\)'ye kadar toplarsak

$$\begin{aligned} P_{n+1} &=\sum_{k=0}^{t}\left(5B_n(k)+B_n(k-1)\right) \\ &=5\sum_{k=0}^{t}B_n(k)+\sum_{k=0}^{t-1}B_n(k) \\ &=6P_n-U_n \end{aligned}$$

bulunur. Yeni sınır terimleri ise

$$U_{n+1}=5U_n+B_n(t-1),\qquad V_{n+1}=5V_n+U_n$$

olur. Buradaki eksik terim bir oranla elde edilir:

$$\frac{B_n(t-1)}{B_n(t)}=\frac{\binom{n-1}{t-1}5^{\,n-t}}{\binom{n-1}{t}5^{\,n-1-t}}=\frac{5t}{n-t},$$

yani

$$B_n(t-1)=U_n\cdot \frac{5t}{n-t}.$$

Adım 5: \(n+1\) Asalsa Geçiş

Eğer \(n+1\) asalsa kesme noktası sağa kayar: \(t_{n+1}=t_n+1=t+1\). Böylece toplam bir ek sınır terimi daha içerir:

$$P_{n+1}=6P_n+5V_n.$$

Yeni son dahil terim

$$U_{n+1}=5V_n+U_n,$$

yeni ilk dışarıda kalan terim ise

$$V_{n+1}=5B_n(t+2)+V_n$$

bağıntısını sağlar. Görülmeyen terim yine oranla bulunur:

$$\frac{B_n(t+2)}{B_n(t+1)}=\frac{\binom{n-1}{t+2}5^{\,n-t-3}}{\binom{n-1}{t+1}5^{\,n-t-2}}=\frac{n-t-2}{5(t+2)},$$

dolayısıyla

$$B_n(t+2)=V_n\cdot \frac{n-t-2}{5(t+2)}.$$

Eğer \(t+2>n-1\) ise bu terim doğal olarak \(0\) olur; uygulamalar da tam olarak bu durumu ele alır.

Adım 6: Modüler Aritmetik ve Son Toplam

Her adımın katkısı

$$C(n)=6P_n$$

olduğundan

$$S(L)\equiv \sum_{n=1}^{L} 6P_n \pmod{10^9+7}$$

yazılır. Rekürsiyonda \(n-t\), \(t+2\) ve \(5\) ile bölmeler vardır. Modül \(10^9+7\) asal olduğundan ve hedef aralıkta bu paydaların hepsi modülden küçük pozitif sayılar olduğundan, tüm bölmeler modüler ters ile çarpmaya dönüştürülür.

Doğrulama Noktaları

Türetilen formüller uygulamalarda kullanılan kesin kontrol değerlerini verir:

$$C(3)=216,\qquad C(4)=1290,\qquad C(11)=361912500,$$

$$C(24)=4727547363281250000,$$

ve kümülatif toplam için

$$S(50)\equiv 832833871 \pmod{10^9+7}.$$

Kodun Çalışma Mantığı

C++, Python ve Java uygulamaları aynı planı uygular. Önce \(L+1\)'e kadar bir asallık tablosu oluşturulur; çünkü hangi güncellemenin kullanılacağını belirleyen tek bilgi \(n+1\)'in asal olup olmadığıdır. Sonra \(L+2\)'ye kadar modüler tersler önceden hesaplanır; böylece sınır oranlarındaki kesirler sabit zamanlı modüler çarpımlara dönüşür. Son aşamada tek bir ileri döngü, kesilmiş toplam ile iki sınır terimini güncel tutar, asal ve bileşik durumlara göre bu değerleri yeniler ve her \(n\) için \(6P_n\) katkısını cevaba ekler.

Karmaşıklık Analizi

Asal eleği \(O(L\log\log L)\) zaman gerektirir. Modüler terslerin ön hesabı \(O(L)\) zamandır. Rekürsiyonun kendisi her \(n\) için sabit sayıda işlem yaptığı için \(O(L)\) sürer. Toplam karmaşıklık \(O(L\log\log L)\) zaman ve \(O(L)\) bellek olup, asıl durum değişkenleri yalnızca \(O(1)\) yer kaplar.

Dipnotlar ve Kaynakça

  1. Problem sayfası: https://projecteuler.net/problem=423
  2. Binom katsayısı ve Pascal özdeşliği: Wikipedia — Binom katsayısı
  3. Asal sayma fonksiyonu \(\pi(n)\): Wikipedia — Prime-counting function
  4. Eratosthenes eleği: Wikipedia — Eratosthenes kalburu
  5. Modüler çarpımsal ters: Wikipedia — Modular multiplicative inverse

Resumen del Problema

Para cada \(n\), las implementaciones usan la fórmula exacta

$$C(n)=6\sum_{k=0}^{\pi(n)} \binom{n-1}{k}5^{\,n-1-k},$$

donde \(\pi(n)\) es el número de primos menores o iguales que \(n\). El objetivo final es la suma acumulada

$$S(L)=\sum_{n=1}^{L} C(n)\pmod{10^9+7},\qquad L=50{,}000{,}000.$$

Evaluar desde cero cada suma binomial truncada sería demasiado costoso, así que la solución construye una transición de tiempo constante entre \(n\) y \(n+1\).

Enfoque Matemático

Paso 1: Separar el Sumando

Definimos

$$B_n(k)=\binom{n-1}{k}5^{\,n-1-k},\qquad t_n=\pi(n).$$

Entonces la suma truncada interior es

$$P_n=\sum_{k=0}^{t_n} B_n(k),$$

y la contribución en el nivel \(n\) queda

$$C(n)=6P_n.$$

Por tanto, el problema se reduce a actualizar \(P_n\) de forma eficiente.

Paso 2: La Identidad de Pascal Produce la Transición

A partir de

$$\binom{n}{k}=\binom{n-1}{k}+\binom{n-1}{k-1},$$

se obtiene

$$B_{n+1}(k)=\binom{n}{k}5^{\,n-k}=5B_n(k)+B_n(k-1).$$

Ésta es la observación central: cada término de la fila siguiente depende solo de dos términos vecinos de la fila actual.

Paso 3: Solo Hace Falta la Frontera

El corte \(t_n=\pi(n)\) cambia como mucho en una unidad cuando \(n\) aumenta. Por eso basta con guardar

$$P_n=\sum_{k=0}^{t_n} B_n(k),\qquad U_n=B_n(t_n),\qquad V_n=B_n(t_n+1).$$

Estas cantidades son, respectivamente, la suma truncada, el último término incluido y el primer término excluido. El estado inicial en \(n=1\) es

$$t_1=\pi(1)=0,\qquad P_1=1,\qquad U_1=1,\qquad V_1=0.$$

Paso 4: Transición Cuando \(n+1\) Es Compuesto

Si \(n+1\) es compuesto, entonces \(t_{n+1}=t_n=t\). Sumando la transición hasta \(k=t\) se obtiene

$$\begin{aligned} P_{n+1} &=\sum_{k=0}^{t}\left(5B_n(k)+B_n(k-1)\right) \\ &=5\sum_{k=0}^{t}B_n(k)+\sum_{k=0}^{t-1}B_n(k) \\ &=6P_n-U_n. \end{aligned}$$

Los nuevos términos de frontera satisfacen

$$U_{n+1}=5U_n+B_n(t-1),\qquad V_{n+1}=5V_n+U_n.$$

El término faltante se recupera mediante un cociente:

$$\frac{B_n(t-1)}{B_n(t)}=\frac{\binom{n-1}{t-1}5^{\,n-t}}{\binom{n-1}{t}5^{\,n-1-t}}=\frac{5t}{n-t},$$

de modo que

$$B_n(t-1)=U_n\cdot \frac{5t}{n-t}.$$

Paso 5: Transición Cuando \(n+1\) Es Primo

Si \(n+1\) es primo, la frontera se desplaza a la derecha: \(t_{n+1}=t_n+1=t+1\). Entonces la suma truncada incluye un término adicional:

$$P_{n+1}=6P_n+5V_n.$$

El nuevo último término incluido es

$$U_{n+1}=5V_n+U_n,$$

y el nuevo primer término excluido cumple

$$V_{n+1}=5B_n(t+2)+V_n.$$

Otra razón cerrada da el término invisible:

$$\frac{B_n(t+2)}{B_n(t+1)}=\frac{\binom{n-1}{t+2}5^{\,n-t-3}}{\binom{n-1}{t+1}5^{\,n-t-2}}=\frac{n-t-2}{5(t+2)},$$

por lo tanto

$$B_n(t+2)=V_n\cdot \frac{n-t-2}{5(t+2)}.$$

Si \(t+2>n-1\), ese término vale \(0\), exactamente como hace la implementación.

Paso 6: Aritmética Modular y Suma Final

Cada paso aporta

$$C(n)=6P_n,$$

así que

$$S(L)\equiv \sum_{n=1}^{L} 6P_n \pmod{10^9+7}.$$

La recurrencia contiene divisiones por \(n-t\), \(t+2\) y \(5\). Como el módulo \(10^9+7\) es primo y todos esos valores son positivos y menores que el módulo en el rango objetivo, cada división se reemplaza por una multiplicación con el inverso modular correspondiente.

Comprobaciones

Las fórmulas anteriores reproducen los valores exactos de control usados por las implementaciones:

$$C(3)=216,\qquad C(4)=1290,\qquad C(11)=361912500,$$

$$C(24)=4727547363281250000,$$

y para la suma acumulada

$$S(50)\equiv 832833871 \pmod{10^9+7}.$$

Cómo Funciona el Código

Las implementaciones en C++, Python y Java siguen la misma estructura. Primero construyen una tabla de primalidad hasta \(L+1\), porque la única decisión lógica es si \(n+1\) es primo o compuesto. Después precalculan inversos modulares hasta \(L+2\), convirtiendo los cocientes de frontera en multiplicaciones modulares de tiempo constante. Por último, una sola pasada hacia adelante mantiene la suma truncada y los dos términos de frontera, los actualiza con las fórmulas del caso primo o compuesto y añade \(6P_n\) a la respuesta en cada iteración.

Análisis de Complejidad

La criba cuesta \(O(L\log\log L)\) tiempo. El precálculo de inversos modulares cuesta \(O(L)\). La recurrencia realiza solo un número constante de operaciones por cada \(n\), así que el bucle principal es \(O(L)\). En conjunto, el método usa \(O(L\log\log L)\) tiempo y \(O(L)\) memoria, mientras que el estado de la recurrencia ocupa solo \(O(1)\).

Referencias

  1. Página del problema: https://projecteuler.net/problem=423
  2. Coeficiente binomial e identidad de Pascal: Wikipedia — Coeficiente binomial
  3. Función contadora de primos \(\pi(n)\): Wikipedia — Función contadora de primos
  4. Criba de Eratóstenes: Wikipedia — Criba de Eratóstenes
  5. Inverso multiplicativo modular: Wikipedia — Inverso multiplicativo modular

问题概述

对每个 \(n\),实现中使用的精确计数公式是

$$C(n)=6\sum_{k=0}^{\pi(n)} \binom{n-1}{k}5^{\,n-1-k},$$

其中 \(\pi(n)\) 表示不超过 \(n\) 的素数个数。最终要求的是累计和

$$S(L)=\sum_{n=1}^{L} C(n)\pmod{10^9+7},\qquad L=50{,}000{,}000.$$

如果对每个 \(n\) 都从头计算一次截断二项式和,代价会非常高,因此解法要推导出从 \(n\) 到 \(n+1\) 的常数时间转移。

数学方法

步骤 1:先定义单项

$$B_n(k)=\binom{n-1}{k}5^{\,n-1-k},\qquad t_n=\pi(n).$$

于是截断内和为

$$P_n=\sum_{k=0}^{t_n} B_n(k),$$

而第 \(n\) 层的贡献就是

$$C(n)=6P_n.$$

因此问题变成怎样高效地更新 \(P_n\)。

步骤 2:利用 Pascal 恒等式得到行转移

$$\binom{n}{k}=\binom{n-1}{k}+\binom{n-1}{k-1}$$

可得

$$B_{n+1}(k)=\binom{n}{k}5^{\,n-k}=5B_n(k)+B_n(k-1).$$

这是整个方法的核心。下一行的每一项只依赖当前行的两个相邻项,因此没有必要保留整行数据。

步骤 3:只需要保存边界状态

阈值 \(t_n=\pi(n)\) 在 \(n\) 增加时最多只会增加 \(1\)。所以只需维护三项信息:

$$P_n=\sum_{k=0}^{t_n} B_n(k),\qquad U_n=B_n(t_n),\qquad V_n=B_n(t_n+1).$$

它们分别表示截断和、最后一个被包含的项,以及第一个被排除的项。初始状态来自 \(n=1\):

$$t_1=\pi(1)=0,\qquad P_1=1,\qquad U_1=1,\qquad V_1=0.$$

步骤 4:当 \(n+1\) 为合数时

若 \(n+1\) 是合数,则 \(t_{n+1}=t_n=t\)。把转移式从 \(k=0\) 加到 \(k=t\),得到

$$\begin{aligned} P_{n+1} &=\sum_{k=0}^{t}\left(5B_n(k)+B_n(k-1)\right) \\ &=5\sum_{k=0}^{t}B_n(k)+\sum_{k=0}^{t-1}B_n(k) \\ &=6P_n-U_n. \end{aligned}$$

新的边界项满足

$$U_{n+1}=5U_n+B_n(t-1),\qquad V_{n+1}=5V_n+U_n.$$

缺失的 \(B_n(t-1)\) 可以通过比值恢复:

$$\frac{B_n(t-1)}{B_n(t)}=\frac{\binom{n-1}{t-1}5^{\,n-t}}{\binom{n-1}{t}5^{\,n-1-t}}=\frac{5t}{n-t},$$

因此

$$B_n(t-1)=U_n\cdot \frac{5t}{n-t}.$$

步骤 5:当 \(n+1\) 为素数时

若 \(n+1\) 是素数,则阈值右移一格:\(t_{n+1}=t_n+1=t+1\)。这时截断和会多包含一个边界项:

$$P_{n+1}=6P_n+5V_n.$$

新的最后包含项为

$$U_{n+1}=5V_n+U_n,$$

新的第一个排除项满足

$$V_{n+1}=5B_n(t+2)+V_n.$$

同样用比值求出看不见的那一项:

$$\frac{B_n(t+2)}{B_n(t+1)}=\frac{\binom{n-1}{t+2}5^{\,n-t-3}}{\binom{n-1}{t+1}5^{\,n-t-2}}=\frac{n-t-2}{5(t+2)},$$

从而

$$B_n(t+2)=V_n\cdot \frac{n-t-2}{5(t+2)}.$$

如果 \(t+2>n-1\),那么该项自然为 \(0\),实现中的处理也是如此。

步骤 6:模运算与最终累加

每一步贡献

$$C(n)=6P_n,$$

于是

$$S(L)\equiv \sum_{n=1}^{L} 6P_n \pmod{10^9+7}.$$

递推中出现了对 \(n-t\)、\(t+2\) 和 \(5\) 的除法。由于模数 \(10^9+7\) 是素数,而这些分母在目标范围内都为正且严格小于模数,所以都可以改写为乘以相应的模逆元。

校验点

上述推导能够重现实现里使用的精确检查值:

$$C(3)=216,\qquad C(4)=1290,\qquad C(11)=361912500,$$

$$C(24)=4727547363281250000,$$

并且累计和满足

$$S(50)\equiv 832833871 \pmod{10^9+7}.$$

代码如何实现

C++、Python 和 Java 实现采用完全相同的结构。首先建立到 \(L+1\) 为止的素性表,因为递推只需要知道 \(n+1\) 是否为素数。接着预计算到 \(L+2\) 的模逆元,把边界比值中的分式全部变成常数时间的模乘。随后只需一次线性前向扫描,就能持续维护截断和以及两个边界项,按“下一个数是素数还是合数”选择对应公式更新,并在每个 \(n\) 把 \(6P_n\) 加入答案。

复杂度分析

素数筛需要 \(O(L\log\log L)\) 时间。模逆元预处理需要 \(O(L)\) 时间。递推主循环对每个 \(n\) 只做常数次模运算,因此是 \(O(L)\)。总时间复杂度为 \(O(L\log\log L)\),总空间复杂度为 \(O(L)\),其中递推状态本身只占 \(O(1)\) 空间。

参考资料

  1. 题目页面: https://projecteuler.net/problem=423
  2. 二项式系数与 Pascal 恒等式: Wikipedia — 二项式系数
  3. 素数计数函数 \(\pi(n)\): Wikipedia — 素数计数函数
  4. 埃拉托斯特尼筛法: Wikipedia — 埃拉托斯特尼筛法
  5. 模逆元: Wikipedia — 模逆元

Краткое описание задачи

Для каждого \(n\) реализации используют точную формулу

$$C(n)=6\sum_{k=0}^{\pi(n)} \binom{n-1}{k}5^{\,n-1-k},$$

где \(\pi(n)\) обозначает число простых, не превосходящих \(n\). Нужно вычислить накопленную сумму

$$S(L)=\sum_{n=1}^{L} C(n)\pmod{10^9+7},\qquad L=50{,}000{,}000.$$

Если пересчитывать каждую усечённую биномиальную сумму отдельно, решение будет слишком медленным, поэтому используется переход за \(O(1)\) от \(n\) к \(n+1\).

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

Шаг 1: Выделим отдельный член

Обозначим

$$B_n(k)=\binom{n-1}{k}5^{\,n-1-k},\qquad t_n=\pi(n).$$

Тогда усечённая внутренняя сумма равна

$$P_n=\sum_{k=0}^{t_n} B_n(k),$$

и вклад уровня \(n\) записывается как

$$C(n)=6P_n.$$

Следовательно, задача сводится к быстрому обновлению \(P_n\).

Шаг 2: Тождество Паскаля даёт переход между строками

Из равенства

$$\binom{n}{k}=\binom{n-1}{k}+\binom{n-1}{k-1}$$

следует

$$B_{n+1}(k)=\binom{n}{k}5^{\,n-k}=5B_n(k)+B_n(k-1).$$

Это основной структурный факт: каждый элемент следующей строки зависит только от двух соседних элементов текущей строки.

Шаг 3: Достаточно хранить только границу

Порог \(t_n=\pi(n)\) при увеличении \(n\) меняется не более чем на единицу. Поэтому достаточно поддерживать

$$P_n=\sum_{k=0}^{t_n} B_n(k),\qquad U_n=B_n(t_n),\qquad V_n=B_n(t_n+1).$$

Это, соответственно, усечённая сумма, последний включённый член и первый исключённый член. Начальное состояние для \(n=1\) таково:

$$t_1=\pi(1)=0,\qquad P_1=1,\qquad U_1=1,\qquad V_1=0.$$

Шаг 4: Переход, когда \(n+1\) составное

Если \(n+1\) составное, то \(t_{n+1}=t_n=t\). Суммируя переход по \(k\) до \(t\), получаем

$$\begin{aligned} P_{n+1} &=\sum_{k=0}^{t}\left(5B_n(k)+B_n(k-1)\right) \\ &=5\sum_{k=0}^{t}B_n(k)+\sum_{k=0}^{t-1}B_n(k) \\ &=6P_n-U_n. \end{aligned}$$

Новые граничные члены равны

$$U_{n+1}=5U_n+B_n(t-1),\qquad V_{n+1}=5V_n+U_n.$$

Недостающий член восстанавливается по отношению:

$$\frac{B_n(t-1)}{B_n(t)}=\frac{\binom{n-1}{t-1}5^{\,n-t}}{\binom{n-1}{t}5^{\,n-1-t}}=\frac{5t}{n-t},$$

то есть

$$B_n(t-1)=U_n\cdot \frac{5t}{n-t}.$$

Шаг 5: Переход, когда \(n+1\) простое

Если \(n+1\) простое, то порог сдвигается вправо: \(t_{n+1}=t_n+1=t+1\). Тогда в усечённую сумму входит ещё один граничный член:

$$P_{n+1}=6P_n+5V_n.$$

Новый последний включённый член равен

$$U_{n+1}=5V_n+U_n,$$

а новый первый исключённый член удовлетворяет

$$V_{n+1}=5B_n(t+2)+V_n.$$

Ещё одно отношение даёт скрытый член:

$$\frac{B_n(t+2)}{B_n(t+1)}=\frac{\binom{n-1}{t+2}5^{\,n-t-3}}{\binom{n-1}{t+1}5^{\,n-t-2}}=\frac{n-t-2}{5(t+2)},$$

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

$$B_n(t+2)=V_n\cdot \frac{n-t-2}{5(t+2)}.$$

Если \(t+2>n-1\), этот член равен \(0\); именно так поступают реализации.

Шаг 6: Модульная арифметика и итоговая сумма

Каждый шаг даёт вклад

$$C(n)=6P_n,$$

поэтому

$$S(L)\equiv \sum_{n=1}^{L} 6P_n \pmod{10^9+7}.$$

В рекуррентных формулах есть деление на \(n-t\), \(t+2\) и \(5\). Поскольку модуль \(10^9+7\) прост, а все эти знаменатели в целевом диапазоне положительны и меньше модуля, деление заменяется умножением на соответствующие модульные обратные.

Проверочные значения

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

$$C(3)=216,\qquad C(4)=1290,\qquad C(11)=361912500,$$

$$C(24)=4727547363281250000,$$

а также

$$S(50)\equiv 832833871 \pmod{10^9+7}.$$

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

Реализации на C++, Python и Java устроены одинаково. Сначала строится таблица простоты до \(L+1\), потому что единственный выбор в рекурсии зависит от того, является ли число \(n+1\) простым. Затем заранее вычисляются модульные обратные до \(L+2\), чтобы все дробные граничные коэффициенты превратились в постоянное число модульных умножений. После этого один линейный проход поддерживает усечённую сумму и два граничных члена, обновляет их по формуле для простого или составного случая и на каждом шаге прибавляет \(6P_n\) к ответу.

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

Решето требует \(O(L\log\log L)\) времени. Предвычисление модульных обратных занимает \(O(L)\) времени. Основной цикл рекурсии делает лишь константное число операций для каждого \(n\), то есть работает за \(O(L)\). Итого получаем \(O(L\log\log L)\) по времени и \(O(L)\) по памяти; само рекуррентное состояние использует только \(O(1)\) памяти.

Дополнительные материалы

  1. Условие задачи: https://projecteuler.net/problem=423
  2. Биномиальный коэффициент и тождество Паскаля: Wikipedia — Биномиальный коэффициент
  3. Функция подсчёта простых \(\pi(n)\): Wikipedia — Функция подсчёта простых
  4. Решето Эратосфена: Wikipedia — Решето Эратосфена
  5. Модульный обратный элемент: Wikipedia — Обратный элемент

ملخص المسألة

لكل \(n\)، تستخدم التطبيقات الصيغة الدقيقة

$$C(n)=6\sum_{k=0}^{\pi(n)} \binom{n-1}{k}5^{\,n-1-k},$$

حيث تمثل \(\pi(n)\) عدد الأعداد الأولية التي لا تتجاوز \(n\). والمطلوب في النهاية هو حساب المجموع التراكمي

$$S(L)=\sum_{n=1}^{L} C(n)\pmod{10^9+7},\qquad L=50{,}000{,}000.$$

إعادة حساب هذا المجموع المبتور من البداية لكل قيمة \(n\) ستكون بطيئة جداً، لذلك يعتمد الحل على اشتقاق انتقال بزمن ثابت من \(n\) إلى \(n+1\).

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

الخطوة 1: عزل الحد الأساسي

نعرّف

$$B_n(k)=\binom{n-1}{k}5^{\,n-1-k},\qquad t_n=\pi(n).$$

وعندئذ يصبح المجموع الداخلي المبتور

$$P_n=\sum_{k=0}^{t_n} B_n(k),$$

فتكون مساهمة المستوى \(n\) هي

$$C(n)=6P_n.$$

إذن جوهر المسألة هو تحديث \(P_n\) بكفاءة عالية.

الخطوة 2: هوية باسكال تعطي انتقال الصف

من الهوية

$$\binom{n}{k}=\binom{n-1}{k}+\binom{n-1}{k-1}$$

نحصل على

$$B_{n+1}(k)=\binom{n}{k}5^{\,n-k}=5B_n(k)+B_n(k-1).$$

وهذه هي الفكرة المركزية: كل حد في الصف التالي يعتمد فقط على حدين متجاورين في الصف الحالي، لذلك لا نحتاج إلى تخزين الصف كاملاً.

الخطوة 3: المهم هو حدود القطع فقط

العتبة \(t_n=\pi(n)\) لا تتغير عند زيادة \(n\) إلا بمقدار \(1\) كحد أقصى. لذلك يكفي الاحتفاظ بالكميات الثلاث الآتية:

$$P_n=\sum_{k=0}^{t_n} B_n(k),\qquad U_n=B_n(t_n),\qquad V_n=B_n(t_n+1).$$

وهذه تمثل على الترتيب: المجموع المبتور، وآخر حد داخل المجموع، وأول حد خارجه. أما الحالة الابتدائية عند \(n=1\) فهي

$$t_1=\pi(1)=0,\qquad P_1=1,\qquad U_1=1,\qquad V_1=0.$$

الخطوة 4: الانتقال عندما يكون \(n+1\) عدداً غير أولي

إذا كان \(n+1\) مركباً فإن \(t_{n+1}=t_n=t\). بجمع علاقة الانتقال حتى \(k=t\) نحصل على

$$\begin{aligned} P_{n+1} &=\sum_{k=0}^{t}\left(5B_n(k)+B_n(k-1)\right) \\ &=5\sum_{k=0}^{t}B_n(k)+\sum_{k=0}^{t-1}B_n(k) \\ &=6P_n-U_n. \end{aligned}$$

أما حداّ الحافة الجديدان فيحققان

$$U_{n+1}=5U_n+B_n(t-1),\qquad V_{n+1}=5V_n+U_n.$$

والحد المفقود يُستعاد من نسبة مغلقة:

$$\frac{B_n(t-1)}{B_n(t)}=\frac{\binom{n-1}{t-1}5^{\,n-t}}{\binom{n-1}{t}5^{\,n-1-t}}=\frac{5t}{n-t},$$

ومنها

$$B_n(t-1)=U_n\cdot \frac{5t}{n-t}.$$

الخطوة 5: الانتقال عندما يكون \(n+1\) عدداً أولياً

إذا كان \(n+1\) أولياً فإن العتبة تتحرك إلى اليمين: \(t_{n+1}=t_n+1=t+1\). عندها يدخل حد إضافي في المجموع المبتور:

$$P_{n+1}=6P_n+5V_n.$$

ويصبح آخر حد داخل المجموع

$$U_{n+1}=5V_n+U_n,$$

أما أول حد خارج المجموع فيحقق

$$V_{n+1}=5B_n(t+2)+V_n.$$

وبنسبة أخرى نحصل على الحد غير المرئي:

$$\frac{B_n(t+2)}{B_n(t+1)}=\frac{\binom{n-1}{t+2}5^{\,n-t-3}}{\binom{n-1}{t+1}5^{\,n-t-2}}=\frac{n-t-2}{5(t+2)},$$

لذلك

$$B_n(t+2)=V_n\cdot \frac{n-t-2}{5(t+2)}.$$

وإذا كان \(t+2>n-1\) فإن هذا الحد يساوي \(0\)، وهذا بالضبط ما تعتمده التطبيقات.

الخطوة 6: الحساب المعياري والتجميع النهائي

كل خطوة تضيف

$$C(n)=6P_n,$$

وبالتالي

$$S(L)\equiv \sum_{n=1}^{L} 6P_n \pmod{10^9+7}.$$

تتضمن العلاقة التكرارية قسمة على \(n-t\) و\(t+2\) و\(5\). وبما أن \(10^9+7\) عدد أولي، وجميع هذه المقامات موجبة وأصغر من المعيار ضمن المجال المطلوب، فيمكن استبدال كل قسمة بضرب في المعكوس المعياري المناسب.

قيم تحقق

الصيغ السابقة تعيد إنتاج قيم التحقق الدقيقة المستخدمة في التطبيقات:

$$C(3)=216,\qquad C(4)=1290,\qquad C(11)=361912500,$$

$$C(24)=4727547363281250000,$$

وكذلك

$$S(50)\equiv 832833871 \pmod{10^9+7}.$$

كيف تعمل الشيفرة

تتبع تطبيقات C++ وPython وJava الخطة نفسها. أولاً تُبنى قائمة أولية حتى \(L+1\)، لأن القرار الوحيد في الانتقال هو ما إذا كان \(n+1\) أولياً أم لا. بعد ذلك تُحسب المعكوسات المعيارية حتى \(L+2\) مسبقاً، فيتحول كل كسر في نسب الحافة إلى ضرب معياري بزمن ثابت. ثم يمر التنفيذ مرة واحدة على القيم من \(1\) إلى \(L\)، محافظاً على المجموع المبتور وحدّي الحافة، ومحدّثاً هذه القيم وفق حالة الأولية أو التركيب، ثم يضيف \(6P_n\) إلى الجواب في كل خطوة.

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

تكلفة الغربلة هي \(O(L\log\log L)\) زمناً. أما حساب المعكوسات المعيارية مسبقاً فيكلف \(O(L)\) زمناً. الحلقة التكرارية نفسها تنفذ عدداً ثابتاً من العمليات لكل \(n\)، لذا فهي \(O(L)\). بالتالي يكون التعقيد الكلي \(O(L\log\log L)\) زمناً و\(O(L)\) ذاكرة، بينما حالة التكرار نفسها لا تحتاج إلا إلى \(O(1)\) إضافية.

مراجع إضافية

  1. صفحة المسألة: https://projecteuler.net/problem=423
  2. المعاملات الثنائية وهوية باسكال: Wikipedia — معامل ثنائي
  3. دالة عدّ الأعداد الأولية \(\pi(n)\): Wikipedia — دالة عدّ الأعداد الأولية
  4. غربال إراتوستينس: Wikipedia — غربال إراتوستينس
  5. المعكوس الضربي المعياري: Wikipedia — معكوس ضربي معياري