Problem Summary

We generate the first \(n\) terms of the deterministic sequence

$$s_0=290797,\qquad s_{k+1}\equiv s_k^2 \pmod{50515093},$$

sort those values into

$$a_0\le a_1\le \dots \le a_{n-1},$$

and consider every product \(a_i a_j\) with \(0\le i<j<n\). There are

$$m=\binom{n}{2}=\frac{n(n-1)}{2}$$

such products. The task is to return their median. In the order-statistic language used by the implementations, this means the element of rank

$$t=\left\lceil\frac{m}{2}\right\rceil=\left\lfloor\frac{m+1}{2}\right\rfloor,$$

so when \(m\) is even, we want the lower of the two middle products.

Mathematical Approach

The central observation is that we do not need to list all \(\binom{n}{2}\) products. Instead, we can ask a threshold question: for a candidate value \(x\), how many pair products are at most \(x\)? Once that counting problem is solved efficiently, the median follows from binary search.

Step 1: Generate the sequence and sort it

The recurrence produces \(n\) positive integers. After sorting them, the array becomes monotone:

$$a_0\le a_1\le \dots \le a_{n-1}.$$

This sorted order is the key structural fact. For a fixed left index \(i\), the products

$$a_i a_{i+1},\ a_i a_{i+2},\ \dots,\ a_i a_{n-1}$$

also increase as the right index moves to the right. That monotonicity is what makes a linear counting pass possible.

Step 2: Turn the median into a counting threshold

For any integer \(x\ge 0\), define

$$F(x)=\#\left\{(i,j):0\le i<j<n,\ a_i a_j\le x\right\}.$$

This function is monotone nondecreasing: if \(x_1\le x_2\), then every pair counted by \(F(x_1)\) is also counted by \(F(x_2)\). Therefore the answer is exactly

$$\min\left\{x\in\mathbb{Z}_{\ge 0}:F(x)\ge t\right\}.$$

That statement is just another way of saying: find the smallest threshold that includes at least the first half of the sorted products.

Step 3: Count \(F(x)\) in linear time with two pointers

Fix a threshold \(x\). For each left index \(i\), let \(J(i)\) be the largest index with \(J(i)>i\) and

$$a_i a_{J(i)}\le x.$$

Then every index between \(i+1\) and \(J(i)\) is also valid, so the contribution of row \(i\) is

$$\max(0, J(i)-i).$$

Hence

$$F(x)=\sum_{i=0}^{n-1}\max(0, J(i)-i).$$

The reason this is fast is that \(J(i)\) never increases as \(i\) increases. Once the left factor becomes larger, the right boundary cannot move to the right and still keep the product \(\le x\). So we can keep one pointer \(j\) starting at \(n-1\), move it only left while \(a_i a_j>x\), and add \(j-i\) when \(j>i\). Because that pointer moves left at most \(n-1\) times in total, one evaluation of \(F(x)\) costs \(O(n)\), not \(O(n^2)\).

Step 4: Binary-search the answer value

The answer must lie between the smallest possible threshold and the largest possible pair product:

$$0\le x\le a_{n-2}a_{n-1}.$$

Now apply a lower-bound binary search on that integer interval. At each midpoint \(\mathrm{mid}\):

$$F(\mathrm{mid})\ge t \Rightarrow \mathrm{hi}=\mathrm{mid},\qquad F(\mathrm{mid})<t \Rightarrow \mathrm{lo}=\mathrm{mid}+1.$$

When the search finishes, \(\mathrm{lo}=\mathrm{hi}\) is the smallest threshold whose count reaches rank \(t\), so it is exactly the median product.

Step 5: Worked Example with the first four generated values

The first four sequence values are already in increasing order:

$$290797,\quad 629527,\quad 13339144,\quad 15552512.$$

So \(n=4\) gives

$$m=\binom{4}{2}=6,\qquad t=\left\lceil\frac{6}{2}\right\rceil=3.$$

The six pair products are

$$\begin{aligned} 290797\cdot 629527&=183064563019,\\ 290797\cdot 13339144&=3878983057768,\\ 290797\cdot 15552512&=4522623832064,\\ 629527\cdot 13339144&=8397351304888,\\ 629527\cdot 15552512&=9790726221824,\\ 13339144\cdot 15552512&=207457197129728. \end{aligned}$$

After sorting, the third product is

$$4522623832064.$$

Equivalently, if \(x=4{,}000{,}000{,}000{,}000\), then only the first two products are counted, so \(F(x)=2<3\). If \(x=4{,}600{,}000{,}000{,}000\), then the first three products are counted, so \(F(x)=3\ge 3\). The lower-bound search therefore locks onto

$$4522623832064,$$

which is the correct median for this small instance.

How the Code Works

The C++, Python, and Java implementations all follow the same pipeline. They first generate the first \(n\) terms of the recurrence, store them in an array, and sort the array. Next they compute the total number of pair products \(m=\binom{n}{2}\) and the target rank \(t=\left\lfloor\frac{m+1}{2}\right\rfloor\).

They then binary-search over the integer interval \([0, a_{n-2}a_{n-1}]\). Each midpoint invokes the linear two-pointer counting routine described above: a left index scans from the beginning, a right index only moves left, and the routine accumulates how many products are at most the current threshold. If the count is already at least \(t\), the search keeps the left half; otherwise it keeps the right half.

The implementations also use integer types large enough for both the pair count and the product range. That guarantees the comparison logic remains exact throughout the search.

Complexity Analysis

Generating the sequence costs \(O(n)\). Sorting costs \(O(n\log n)\). Each evaluation of \(F(x)\) is \(O(n)\) because the right pointer moves only left and never resets. The binary search performs \(O(\log V)\) iterations, where

$$V=a_{n-2}a_{n-1}.$$

So the total running time is

$$O(n\log n+n\log V),$$

with memory usage \(O(n)\). In practice the sorting step dominates the preprocessing, while the counting oracle makes each search iteration linear and cache-friendly.

Footnotes and References

  1. Problem page: https://projecteuler.net/problem=793
  2. Order statistic: Wikipedia — Order statistic
  3. Binary search algorithm: Wikipedia — Binary search algorithm
  4. Two pointers technique: cp-algorithms — Two pointers method
  5. Modular arithmetic: Wikipedia — Modular arithmetic

Problemzusammenfassung

Wir erzeugen die ersten \(n\) Glieder der deterministischen Folge

$$s_0=290797,\qquad s_{k+1}\equiv s_k^2 \pmod{50515093},$$

sortieren diese Werte zu

$$a_0\le a_1\le \dots \le a_{n-1},$$

und betrachten alle Produkte \(a_i a_j\) mit \(0\le i<j<n\). Davon gibt es

$$m=\binom{n}{2}=\frac{n(n-1)}{2}.$$

Gesucht ist der Median dieser Produkte. In der Ordnungssprache der Implementierungen ist das das Element mit Rang

$$t=\left\lceil\frac{m}{2}\right\rceil=\left\lfloor\frac{m+1}{2}\right\rfloor,$$

also bei geradem \(m\) die kleinere der beiden mittleren Positionen.

Mathematischer Ansatz

Der entscheidende Punkt ist, dass wir die \(\binom{n}{2}\) Produkte nicht explizit auflisten müssen. Stattdessen fragen wir für einen Kandidaten \(x\): Wie viele Paarprodukte sind höchstens \(x\)? Wenn diese Zählfunktion effizient berechnet werden kann, folgt der Median per binärer Suche.

Schritt 1: Folge erzeugen und sortieren

Die Rekursion liefert \(n\) positive ganze Zahlen. Nach dem Sortieren gilt

$$a_0\le a_1\le \dots \le a_{n-1}.$$

Für festes \(i\) wachsen dann auch die Produkte

$$a_i a_{i+1},\ a_i a_{i+2},\ \dots,\ a_i a_{n-1}$$

mit dem rechten Index. Genau diese Monotonie macht später den linearen Zwei-Zeiger-Durchlauf möglich.

Schritt 2: Den Median als Schwellenwert formulieren

Für jedes \(x\ge 0\) definieren wir

$$F(x)=\#\left\{(i,j):0\le i<j<n,\ a_i a_j\le x\right\}.$$

Die Funktion \(F\) ist monoton nicht fallend. Deshalb ist die gesuchte Antwort genau

$$\min\left\{x\in\mathbb{Z}_{\ge 0}:F(x)\ge t\right\}.$$

Anschaulich suchen wir also den kleinsten Schwellenwert, unter dem bereits mindestens die erste Hälfte aller sortierten Produkte liegt.

Schritt 3: \(F(x)\) in linearer Zeit mit zwei Zeigern zählen

Fixiere ein \(x\). Für jeden linken Index \(i\) sei \(J(i)\) der größte Index mit \(J(i)>i\) und

$$a_i a_{J(i)}\le x.$$

Dann sind automatisch auch alle Indizes zwischen \(i+1\) und \(J(i)\) gültig, also trägt Zeile \(i\) genau

$$\max(0, J(i)-i)$$

zum Zähler bei. Damit gilt

$$F(x)=\sum_{i=0}^{n-1}\max(0, J(i)-i).$$

Der Trick ist, dass \(J(i)\) mit wachsendem \(i\) niemals nach rechts springen kann. Wird der linke Faktor größer, kann der rechte Grenzindex nur gleich bleiben oder nach links wandern. Daher genügt ein einziger rechter Zeiger \(j\), der bei \(n-1\) startet und nur nach links bewegt wird, solange \(a_i a_j>x\) ist. Weil dieser Zeiger insgesamt höchstens \(n-1\) Schritte macht, kostet eine Auswertung von \(F(x)\) nur \(O(n)\).

Schritt 4: Binäre Suche auf dem Wertebereich

Die Antwort liegt zwischen dem kleinsten sinnvollen Schwellenwert und dem größten möglichen Paarprodukt:

$$0\le x\le a_{n-2}a_{n-1}.$$

Darauf führen wir eine Lower-Bound-Binärsuche aus. Für den Mittelpunkt \(\mathrm{mid}\) gilt:

$$F(\mathrm{mid})\ge t \Rightarrow \mathrm{hi}=\mathrm{mid},\qquad F(\mathrm{mid})<t \Rightarrow \mathrm{lo}=\mathrm{mid}+1.$$

Am Ende ist \(\mathrm{lo}=\mathrm{hi}\) genau der kleinste Schwellenwert, der Rang \(t\) erreicht, also der Median.

Schritt 5: Durchgerechnetes Beispiel mit den ersten vier Werten

Die ersten vier erzeugten Werte sind bereits sortiert:

$$290797,\quad 629527,\quad 13339144,\quad 15552512.$$

Für \(n=4\) erhält man

$$m=\binom{4}{2}=6,\qquad t=\left\lceil\frac{6}{2}\right\rceil=3.$$

Die sechs Paarprodukte lauten

$$\begin{aligned} 290797\cdot 629527&=183064563019,\\ 290797\cdot 13339144&=3878983057768,\\ 290797\cdot 15552512&=4522623832064,\\ 629527\cdot 13339144&=8397351304888,\\ 629527\cdot 15552512&=9790726221824,\\ 13339144\cdot 15552512&=207457197129728. \end{aligned}$$

Sortiert ist der dritte Wert

$$4522623832064.$$

Dasselbe sieht man über die Zählfunktion: Für \(x=4{,}000{,}000{,}000{,}000\) gilt \(F(x)=2<3\), für \(x=4{,}600{,}000{,}000{,}000\) dagegen \(F(x)=3\ge 3\). Die binäre Suche landet deshalb exakt bei

$$4522623832064.$$

Wie der Code arbeitet

Die C++-, Python- und Java-Implementierungen verwenden denselben Ablauf. Zuerst werden die ersten \(n\) Folgenglieder erzeugt, in einem Array gespeichert und sortiert. Danach berechnen sie die Gesamtzahl der Paarprodukte \(m=\binom{n}{2}\) und den Zielrang \(t=\left\lfloor\frac{m+1}{2}\right\rfloor\).

Anschließend wird auf dem Intervall \([0, a_{n-2}a_{n-1}]\) binär gesucht. Jeder Mittelpunkt ruft die lineare Zwei-Zeiger-Zählung auf: ein linker Index läuft nach vorn, ein rechter Index bewegt sich nur nach links, und so wird die Zahl der Produkte \(\le\) Schwelle aufsummiert. Ist die Anzahl bereits mindestens \(t\), bleibt die Suche links; andernfalls geht sie nach rechts.

Außerdem verwenden die Implementierungen Ganzzahltypen, die sowohl für die Produktwerte als auch für die Paaranzahl ausreichen. Dadurch bleiben alle Vergleiche exakt.

Komplexitätsanalyse

Die Erzeugung der Folge kostet \(O(n)\), das Sortieren \(O(n\log n)\). Jede Auswertung von \(F(x)\) kostet \(O(n)\), weil der rechte Zeiger nur nach links läuft und nie zurückgesetzt wird. Die binäre Suche benötigt \(O(\log V)\) Iterationen mit

$$V=a_{n-2}a_{n-1}.$$

Insgesamt ergibt sich damit

$$O(n\log n+n\log V)$$

Zeit und \(O(n)\) Speicher. Praktisch dominiert meist das Sortieren, während die Zählroutine pro Suchschritt linear bleibt.

Fußnoten und Quellen

  1. Problemseite: https://projecteuler.net/problem=793
  2. Ordnungsstatistik: Wikipedia — Order statistic
  3. Binäre Suche: Wikipedia — Binary search algorithm
  4. Zwei-Zeiger-Methode: cp-algorithms — Two pointers method
  5. Modulare Arithmetik: Wikipedia — Modular arithmetic

Problem Özeti

Önce şu deterministik dizinin ilk \(n\) terimi üretilir:

$$s_0=290797,\qquad s_{k+1}\equiv s_k^2 \pmod{50515093}.$$

Bu değerler sıralanarak

$$a_0\le a_1\le \dots \le a_{n-1}$$

dizisi elde edilir ve \(0\le i<j<n\) için tüm \(a_i a_j\) çarpımları incelenir. Toplam çarpım sayısı

$$m=\binom{n}{2}=\frac{n(n-1)}{2}$$

kadardır. Aranan değer bu çoklu kümenin medyanıdır. Uygulamanın kullandığı sıra-istatistiği dilinde bu,

$$t=\left\lceil\frac{m}{2}\right\rceil=\left\lfloor\frac{m+1}{2}\right\rfloor$$

sırasındaki elemandır; yani \(m\) çiftse iki ortadaki değerin küçüğü seçilir.

Matematiksel Yaklaşım

Ana fikir, \(\binom{n}{2}\) adet çarpımı tek tek üretmemektir. Bunun yerine şu soruyu sorarız: Verilen bir \(x\) eşiği için, kaç çift çarpımı \(x\)'ten küçüktür ya da eşittir? Bu sayım hızlı yapılabildiğinde, medyan değer uzayında ikili aramayla bulunur.

Adım 1: Diziyi üret ve sırala

Özyineleme \(n\) tane pozitif tamsayı üretir. Sıralamadan sonra

$$a_0\le a_1\le \dots \le a_{n-1}$$

olur. Sabit bir \(i\) için

$$a_i a_{i+1},\ a_i a_{i+2},\ \dots,\ a_i a_{n-1}$$

ürünleri sağ indeks büyüdükçe artar. Bütün yöntemin dayandığı düzen tam olarak budur.

Adım 2: Medyanı eşik sayımına çevir

Her \(x\ge 0\) için

$$F(x)=\#\left\{(i,j):0\le i<j<n,\ a_i a_j\le x\right\}$$

tanımını yapalım. \(F(x)\) monoton artmayan değil, monoton azalmayan bir fonksiyondur: eşik büyüdükçe sayılan çift sayısı azalmaz. Bu yüzden aradığımız cevap tam olarak

$$\min\left\{x\in\mathbb{Z}_{\ge 0}:F(x)\ge t\right\}$$

olur. Başka bir deyişle, sıralı ürünler listesinde en az ilk yarıyı kapsayan en küçük eşik aranır.

Adım 3: \(F(x)\)'i iki işaretçiyle doğrusal zamanda hesapla

Bir \(x\) sabitleyelim. Her sol indeks \(i\) için, \(J(i)\) değerini

$$a_i a_{J(i)}\le x,\qquad J(i)>i$$

koşulunu sağlayan en büyük sağ indeks olarak tanımlayalım. O zaman \(i+1\) ile \(J(i)\) arasındaki tüm indeksler de geçerlidir; dolayısıyla \(i\). satırın katkısı

$$\max(0, J(i)-i)$$

olur ve

$$F(x)=\sum_{i=0}^{n-1}\max(0, J(i)-i)$$

elde edilir. Buradaki kritik nokta, \(i\) büyüdükçe \(J(i)\)'nin asla sağa gitmemesidir. Sol çarpan büyüdüğünde, aynı eşik altında kabul edilen en sağ sınır ancak yerinde kalabilir ya da sola kayabilir. Bu nedenle \(j\) işaretçisi \(n-1\)'den başlayıp yalnızca sola hareket eder. Toplamda en fazla \(n-1\) kez azaldığı için bir sayım geçişi \(O(n)\) sürer.

Adım 4: Cevabı değer aralığında ikili ara

Cevap şu aralıkta olmak zorundadır:

$$0\le x\le a_{n-2}a_{n-1}.$$

Bu aralık üzerinde lower-bound tipi ikili arama yapılır. Orta nokta \(\mathrm{mid}\) için karar kuralı

$$F(\mathrm{mid})\ge t \Rightarrow \mathrm{hi}=\mathrm{mid},\qquad F(\mathrm{mid})<t \Rightarrow \mathrm{lo}=\mathrm{mid}+1$$

şeklindedir. Arama bittiğinde \(\mathrm{lo}=\mathrm{hi}\) olur ve bu değer tam olarak medyan çarpımdır.

Adım 5: İlk dört üretilen değerle çözümlü örnek

İlk dört terim zaten artan sıradadır:

$$290797,\quad 629527,\quad 13339144,\quad 15552512.$$

Burada

$$m=\binom{4}{2}=6,\qquad t=\left\lceil\frac{6}{2}\right\rceil=3$$

olur. Altı çift çarpımı şunlardır:

$$\begin{aligned} 290797\cdot 629527&=183064563019,\\ 290797\cdot 13339144&=3878983057768,\\ 290797\cdot 15552512&=4522623832064,\\ 629527\cdot 13339144&=8397351304888,\\ 629527\cdot 15552512&=9790726221824,\\ 13339144\cdot 15552512&=207457197129728. \end{aligned}$$

Sıralı listede üçüncü değer

$$4522623832064$$

olduğu için medyan budur. Eşik sayımı açısından bakarsak, \(x=4{,}000{,}000{,}000{,}000\) için yalnızca iki ürün sayılır ve \(F(x)=2<3\); \(x=4{,}600{,}000{,}000{,}000\) için ise ilk üç ürün sayılır ve \(F(x)=3\ge 3\). Dolayısıyla ikili arama tam bu değere iner.

Kod Nasıl Çalışır

C++, Python ve Java uygulamalarının akışı aynıdır. Önce dizinin ilk \(n\) terimi üretilir, bir diziye alınır ve sıralanır. Ardından toplam çift sayısı \(m=\binom{n}{2}\) ve hedef sıra \(t=\left\lfloor\frac{m+1}{2}\right\rfloor\) hesaplanır.

Sonra \([0, a_{n-2}a_{n-1}]\) aralığında ikili arama yapılır. Her orta nokta için yukarıdaki iki işaretçili doğrusal sayaç çağrılır: soldaki indeks ilerler, sağdaki indeks yalnızca sola gider ve mevcut eşik altında kalan çiftler toplanır. Sayaç en az \(t\) veriyorsa arama sol yarıda devam eder; aksi halde sağ yarıya geçilir.

Uygulamalar ayrıca ürün aralığını ve çift sayısını güvenle taşıyacak kadar geniş tamsayı türleri kullanır. Böylece tüm karşılaştırmalar kesin kalır.

Karmaşıklık Analizi

Diziyi üretmek \(O(n)\), sıralamak \(O(n\log n)\) zaman alır. Her \(F(x)\) hesabı \(O(n)\)'dir; çünkü sağ işaretçi hiçbir zaman sıfırlanmaz ve sadece sola gider. İkili arama \(O(\log V)\) adım sürer; burada

$$V=a_{n-2}a_{n-1}.$$

Toplam karmaşıklık

$$O(n\log n+n\log V)$$

ve bellek kullanımı \(O(n)\) olur. Pratikte ön işleme tarafında sıralama baskındır; arama sırasındaki her sayaç geçişi ise doğrusal kalır.

Dipnotlar ve Kaynakça

  1. Problem sayfası: https://projecteuler.net/problem=793
  2. Sıra istatistiği: Wikipedia — Order statistic
  3. İkili arama: Wikipedia — Binary search algorithm
  4. Two pointers yöntemi: cp-algorithms — Two pointers method
  5. Modüler aritmetik: Wikipedia — Modular arithmetic

Resumen del Problema

Se generan los primeros \(n\) términos de la sucesión determinista

$$s_0=290797,\qquad s_{k+1}\equiv s_k^2 \pmod{50515093},$$

luego se ordenan para obtener

$$a_0\le a_1\le \dots \le a_{n-1},$$

y se consideran todos los productos \(a_i a_j\) con \(0\le i<j<n\). El número total de productos es

$$m=\binom{n}{2}=\frac{n(n-1)}{2}.$$

La tarea consiste en hallar la mediana de ese multiconjunto. En el lenguaje de estadísticos de orden usado por las implementaciones, eso significa el elemento de rango

$$t=\left\lceil\frac{m}{2}\right\rceil=\left\lfloor\frac{m+1}{2}\right\rfloor,$$

de modo que si \(m\) es par se toma el menor de los dos productos centrales.

Enfoque Matemático

La observación decisiva es que no hace falta materializar los \(\binom{n}{2}\) productos. Basta con responder eficientemente a esta pregunta: dado un umbral \(x\), ¿cuántos productos de pares son \(\le x\)? Una vez resuelto ese conteo, la mediana aparece mediante búsqueda binaria.

Paso 1: Generar la sucesión y ordenarla

La recurrencia produce \(n\) enteros positivos. Tras ordenar, queda

$$a_0\le a_1\le \dots \le a_{n-1}.$$

Para un índice izquierdo fijo \(i\), los productos

$$a_i a_{i+1},\ a_i a_{i+2},\ \dots,\ a_i a_{n-1}$$

crecen al mover el índice derecho hacia la derecha. Esa monotonicidad es exactamente la estructura que explota el conteo lineal.

Paso 2: Convertir la mediana en un problema de umbral

Definimos, para cada \(x\ge 0\),

$$F(x)=\#\left\{(i,j):0\le i<j<n,\ a_i a_j\le x\right\}.$$

La función \(F\) es monótona no decreciente. Por tanto, la respuesta es

$$\min\left\{x\in\mathbb{Z}_{\ge 0}:F(x)\ge t\right\}.$$

Es decir, buscamos el menor umbral que ya cubre al menos la primera mitad de la lista ordenada de productos.

Paso 3: Calcular \(F(x)\) en tiempo lineal con dos punteros

Fijado un valor \(x\), para cada índice izquierdo \(i\) sea \(J(i)\) el mayor índice con \(J(i)>i\) tal que

$$a_i a_{J(i)}\le x.$$

Entonces todos los índices entre \(i+1\) y \(J(i)\) también sirven, así que la contribución de la fila \(i\) es

$$\max(0, J(i)-i).$$

Por ello

$$F(x)=\sum_{i=0}^{n-1}\max(0, J(i)-i).$$

La clave es que \(J(i)\) nunca aumenta cuando \(i\) aumenta. Si el factor izquierdo crece, el límite derecho admisible no puede desplazarse a la derecha. Así, basta un único puntero \(j\) que empieza en \(n-1\) y solo se mueve hacia la izquierda mientras \(a_i a_j>x\). Como ese puntero retrocede a lo sumo \(n-1\) veces en total, una evaluación de \(F(x)\) cuesta \(O(n)\).

Paso 4: Hacer búsqueda binaria sobre el valor final

La respuesta está entre el menor umbral posible y el mayor producto de dos elementos:

$$0\le x\le a_{n-2}a_{n-1}.$$

Sobre ese intervalo entero aplicamos una búsqueda binaria de lower bound. Para el punto medio \(\mathrm{mid}\):

$$F(\mathrm{mid})\ge t \Rightarrow \mathrm{hi}=\mathrm{mid},\qquad F(\mathrm{mid})<t \Rightarrow \mathrm{lo}=\mathrm{mid}+1.$$

Cuando termina, \(\mathrm{lo}=\mathrm{hi}\) es el menor umbral cuyo conteo alcanza el rango \(t\), y por tanto es exactamente la mediana.

Paso 5: Ejemplo trabajado con los cuatro primeros valores

Los cuatro primeros términos generados ya están ordenados:

$$290797,\quad 629527,\quad 13339144,\quad 15552512.$$

Entonces

$$m=\binom{4}{2}=6,\qquad t=\left\lceil\frac{6}{2}\right\rceil=3.$$

Los seis productos son

$$\begin{aligned} 290797\cdot 629527&=183064563019,\\ 290797\cdot 13339144&=3878983057768,\\ 290797\cdot 15552512&=4522623832064,\\ 629527\cdot 13339144&=8397351304888,\\ 629527\cdot 15552512&=9790726221824,\\ 13339144\cdot 15552512&=207457197129728. \end{aligned}$$

El tercer valor de la lista ordenada es

$$4522623832064.$$

También se ve desde el contador: si \(x=4{,}000{,}000{,}000{,}000\), solo se cuentan dos productos y \(F(x)=2<3\); si \(x=4{,}600{,}000{,}000{,}000\), ya se cuentan tres y \(F(x)=3\ge 3\). La búsqueda binaria de lower bound termina exactamente en

$$4522623832064.$$

Cómo Funciona el Código

Las implementaciones en C++, Python y Java siguen el mismo esquema. Primero generan los primeros \(n\) términos de la recurrencia, los guardan en un arreglo y ordenan ese arreglo. Luego calculan el número total de productos \(m=\binom{n}{2}\) y el rango objetivo \(t=\left\lfloor\frac{m+1}{2}\right\rfloor\).

Después realizan una búsqueda binaria sobre el intervalo \([0, a_{n-2}a_{n-1}]\). En cada punto medio invocan el conteo lineal con dos punteros: el índice izquierdo avanza, el derecho solo retrocede, y se acumula cuántos productos quedan por debajo del umbral actual. Si el conteo ya alcanza \(t\), la búsqueda continúa en la mitad izquierda; si no, en la derecha.

Además, las implementaciones usan tipos enteros suficientemente amplios para almacenar tanto el rango de productos como el número de pares. Así se evita cualquier error de comparación.

Análisis de Complejidad

Generar la sucesión cuesta \(O(n)\) y ordenarla cuesta \(O(n\log n)\). Cada evaluación de \(F(x)\) cuesta \(O(n)\), porque el puntero derecho solo se desplaza hacia la izquierda y nunca se reinicia. La búsqueda binaria hace \(O(\log V)\) iteraciones, donde

$$V=a_{n-2}a_{n-1}.$$

Por tanto, el coste total es

$$O(n\log n+n\log V),$$

con memoria \(O(n)\). En la práctica, la ordenación domina el preprocesamiento y el oráculo de conteo mantiene cada iteración de la búsqueda en tiempo lineal.

Notas y Referencias

  1. Página del problema: https://projecteuler.net/problem=793
  2. Estadístico de orden: Wikipedia — Order statistic
  3. Búsqueda binaria: Wikipedia — Binary search algorithm
  4. Técnica de dos punteros: cp-algorithms — Two pointers method
  5. Aritmética modular: Wikipedia — Modular arithmetic

问题概述

题目先生成确定性数列

$$s_0=290797,\qquad s_{k+1}\equiv s_k^2 \pmod{50515093},$$

取前 \(n\) 项并排序,得到

$$a_0\le a_1\le \dots \le a_{n-1}.$$

随后考虑所有满足 \(0\le i<j<n\) 的乘积 \(a_i a_j\)。这样的乘积共有

$$m=\binom{n}{2}=\frac{n(n-1)}{2}$$

个。我们要求的是这个多重集合的中位数。按照实现采用的顺序统计量定义,目标位置是

$$t=\left\lceil\frac{m}{2}\right\rceil=\left\lfloor\frac{m+1}{2}\right\rfloor,$$

因此当 \(m\) 为偶数时,取的是中间两个值里较小的那个。

数学方法

核心思想不是去显式构造全部 \(\binom{n}{2}\) 个乘积,而是把问题改写成一个“阈值计数”问题:给定某个候选值 \(x\),有多少对乘积不超过 \(x\)?一旦这个计数能够快速完成,中位数就可以通过二分搜索得到。

步骤 1:生成并排序基础数列

递推式给出 \(n\) 个正整数。排序之后,数组满足

$$a_0\le a_1\le \dots \le a_{n-1}.$$

对固定的左端点 \(i\),下面这一串乘积

$$a_i a_{i+1},\ a_i a_{i+2},\ \dots,\ a_i a_{n-1}$$

会随着右端点的增大而单调不减。这种单调性是整个算法成立的基础,因为它保证了“满足条件的右端点”在每一行里总是形成一个连续前缀。

步骤 2:把中位数改写成顺序统计问题

对任意整数 \(x\ge 0\),定义

$$F(x)=\#\left\{(i,j):0\le i<j<n,\ a_i a_j\le x\right\}.$$

这个函数是单调不减的:如果 \(x_1\le x_2\),那么所有满足 \(a_i a_j\le x_1\) 的配对当然也满足 \(a_i a_j\le x_2\)。所以真正要求的答案可以写成

$$\min\left\{x\in\mathbb{Z}_{\ge 0}:F(x)\ge t\right\}.$$

也就是说,我们寻找的是最小的阈值 \(x\),使得不超过 \(x\) 的乘积个数已经达到第 \(t\) 个位置。

步骤 3:用双指针在线性时间计算 \(F(x)\)

固定一个阈值 \(x\)。对每个左端点 \(i\),设 \(J(i)\) 是满足 \(J(i)>i\) 且

$$a_i a_{J(i)}\le x$$

的最大下标。那么从 \(i+1\) 到 \(J(i)\) 的所有右端点都会合法,因此第 \(i\) 行贡献

$$\max(0, J(i)-i)$$

个配对,于是

$$F(x)=\sum_{i=0}^{n-1}\max(0, J(i)-i).$$

关键在于 \(J(i)\) 随 \(i\) 的增大绝不会向右移动。原因很简单:左端点变大以后,乘积只会更大,因此原来不合法的右端点不可能重新变合法。于是可以维护一个从最右端 \(n-1\) 开始的指针 \(j\),只要 \(a_i a_j>x\) 就不断左移。对每个 \(i\) 而言,指针最多继续往左退,而不会回头。整个扫描过程中右指针总共只会移动 \(O(n)\) 次,因此一次计数就是 \(O(n)\) 时间,而不是朴素的 \(O(n^2)\)。

步骤 4:在答案空间上做二分搜索

答案显然落在下面的范围内:

$$0\le x\le a_{n-2}a_{n-1}.$$

其中上界是排序后最大的两个元素的乘积,也就是所有配对中的最大乘积。接着在这个整数区间上做 lower bound 型二分。对中点 \(\mathrm{mid}\),更新规则为

$$F(\mathrm{mid})\ge t \Rightarrow \mathrm{hi}=\mathrm{mid},\qquad F(\mathrm{mid})<t \Rightarrow \mathrm{lo}=\mathrm{mid}+1.$$

当二分结束时,\(\mathrm{lo}=\mathrm{hi}\) 就是最小的可行阈值,也正是所求的中位乘积。

步骤 5:用前四个生成值做一个完整示例

前四个生成值恰好已经是升序:

$$290797,\quad 629527,\quad 13339144,\quad 15552512.$$

此时

$$m=\binom{4}{2}=6,\qquad t=\left\lceil\frac{6}{2}\right\rceil=3.$$

六个配对乘积分别是

$$\begin{aligned} 290797\cdot 629527&=183064563019,\\ 290797\cdot 13339144&=3878983057768,\\ 290797\cdot 15552512&=4522623832064,\\ 629527\cdot 13339144&=8397351304888,\\ 629527\cdot 15552512&=9790726221824,\\ 13339144\cdot 15552512&=207457197129728. \end{aligned}$$

按从小到大排序后,第 \(3\) 个值是

$$4522623832064.$$

如果从计数函数角度理解,也完全一致。取 \(x=4{,}000{,}000{,}000{,}000\) 时,只有前两个乘积不超过 \(x\),因此 \(F(x)=2<3\)。取 \(x=4{,}600{,}000{,}000{,}000\) 时,前三个乘积都被计入,因此 \(F(x)=3\ge 3\)。所以 lower bound 二分会把答案收缩到

$$4522623832064.$$

代码如何工作

C++、Python 和 Java 实现遵循同一条流程。第一步是生成递推序列的前 \(n\) 项,存入数组并排序。第二步计算总配对数 \(m=\binom{n}{2}\) 以及目标秩次 \(t=\left\lfloor\frac{m+1}{2}\right\rfloor\)。

随后实现会在区间 \([0, a_{n-2}a_{n-1}]\) 上做整数二分。每一次取中点,都会调用上面的双指针计数过程:左指针从小到大扫描,右指针只向左收缩,并累计当前阈值下有多少个乘积不超过中点。如果计数已经达到 \(t\),说明答案不在右侧,于是保留左半区间;否则保留右半区间。

这些实现还使用了足够宽的整数类型来容纳乘积范围和配对数量,因此整个比较过程都保持精确,不依赖近似计算。

复杂度分析

生成数列需要 \(O(n)\) 时间,排序需要 \(O(n\log n)\) 时间。每次计算 \(F(x)\) 只要 \(O(n)\),因为右指针在一次完整扫描中只会单向左移,不会反复重置。二分搜索一共进行 \(O(\log V)\) 轮,其中

$$V=a_{n-2}a_{n-1}.$$

因此总时间复杂度为

$$O(n\log n+n\log V),$$

空间复杂度为 \(O(n)\)。在实际运行中,排序是主要的预处理成本,而每次阈值判定都保持线性复杂度。

注释与参考资料

  1. 题目页面: https://projecteuler.net/problem=793
  2. 顺序统计量: Wikipedia — Order statistic
  3. 二分查找: Wikipedia — Binary search algorithm
  4. 双指针方法: cp-algorithms — Two pointers method
  5. 模运算: Wikipedia — Modular arithmetic

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

Сначала строятся первые \(n\) членов детерминированной последовательности

$$s_0=290797,\qquad s_{k+1}\equiv s_k^2 \pmod{50515093},$$

после чего они сортируются в неубывающем порядке:

$$a_0\le a_1\le \dots \le a_{n-1}.$$

Затем рассматриваются все произведения \(a_i a_j\) при \(0\le i<j<n\). Таких произведений

$$m=\binom{n}{2}=\frac{n(n-1)}{2}.$$

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

$$t=\left\lceil\frac{m}{2}\right\rceil=\left\lfloor\frac{m+1}{2}\right\rfloor,$$

то есть при четном \(m\) выбирается меньший из двух центральных элементов.

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

Главная идея состоит в том, чтобы не строить все \(\binom{n}{2}\) произведений явно. Вместо этого нужно уметь быстро отвечать на вопрос: для заданного порога \(x\) сколько пар дают произведение не больше \(x\)? После этого медиана находится обычным бинарным поиском по значению ответа.

Шаг 1: Построить последовательность и отсортировать ее

Рекуррентная формула дает \(n\) положительных целых чисел. После сортировки выполняется

$$a_0\le a_1\le \dots \le a_{n-1}.$$

Для фиксированного левого индекса \(i\) произведения

$$a_i a_{i+1},\ a_i a_{i+2},\ \dots,\ a_i a_{n-1}$$

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

Шаг 2: Переформулировать медиану как пороговый подсчет

Для любого \(x\ge 0\) введем функцию

$$F(x)=\#\left\{(i,j):0\le i<j<n,\ a_i a_j\le x\right\}.$$

Она монотонно не убывает. Поэтому искомое значение равно

$$\min\left\{x\in\mathbb{Z}_{\ge 0}:F(x)\ge t\right\}.$$

Иначе говоря, мы ищем минимальный порог, который уже покрывает по крайней мере первую половину отсортированного списка произведений.

Шаг 3: Считать \(F(x)\) за \(O(n)\) методом двух указателей

Зафиксируем \(x\). Для каждого левого индекса \(i\) обозначим через \(J(i)\) наибольший индекс \(J(i)>i\), удовлетворяющий условию

$$a_i a_{J(i)}\le x.$$

Тогда все индексы от \(i+1\) до \(J(i)\) тоже допустимы, и вклад строки \(i\) равен

$$\max(0, J(i)-i).$$

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

$$F(x)=\sum_{i=0}^{n-1}\max(0, J(i)-i).$$

Ключевой факт: при увеличении \(i\) значение \(J(i)\) не может сдвинуться вправо. Если левый множитель стал больше, то допустимая правая граница может только остаться на месте или уйти влево. Значит, достаточно держать один правый указатель \(j\), стартующий в позиции \(n-1\), и двигать его только влево, пока \(a_i a_j>x\). За весь проход этот указатель сместится не более чем на \(n-1\) позиций, поэтому одна проверка \(F(x)\) требует лишь \(O(n)\) времени.

Шаг 4: Бинарный поиск по значению ответа

Ответ лежит в диапазоне

$$0\le x\le a_{n-2}a_{n-1}.$$

Верхняя граница здесь равна максимальному произведению двух элементов отсортированного массива. На этом целочисленном интервале выполняется бинарный поиск типа lower bound. Для середины \(\mathrm{mid}\) правило перехода такое:

$$F(\mathrm{mid})\ge t \Rightarrow \mathrm{hi}=\mathrm{mid},\qquad F(\mathrm{mid})<t \Rightarrow \mathrm{lo}=\mathrm{mid}+1.$$

Когда поиск заканчивается, \(\mathrm{lo}=\mathrm{hi}\) и это минимальный допустимый порог, то есть сама медиана произведений.

Шаг 5: Разобранный пример на первых четырех значениях

Первые четыре сгенерированных значения уже упорядочены:

$$290797,\quad 629527,\quad 13339144,\quad 15552512.$$

Отсюда

$$m=\binom{4}{2}=6,\qquad t=\left\lceil\frac{6}{2}\right\rceil=3.$$

Шесть попарных произведений равны

$$\begin{aligned} 290797\cdot 629527&=183064563019,\\ 290797\cdot 13339144&=3878983057768,\\ 290797\cdot 15552512&=4522623832064,\\ 629527\cdot 13339144&=8397351304888,\\ 629527\cdot 15552512&=9790726221824,\\ 13339144\cdot 15552512&=207457197129728. \end{aligned}$$

Третий элемент в отсортированном порядке равен

$$4522623832064.$$

То же видно через пороговый подсчет: при \(x=4{,}000{,}000{,}000{,}000\) имеем \(F(x)=2<3\), а при \(x=4{,}600{,}000{,}000{,}000\) уже \(F(x)=3\ge 3\). Значит, бинарный поиск сходится именно к

$$4522623832064.$$

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

Реализации на C++, Python и Java используют один и тот же конвейер. Сначала они генерируют первые \(n\) членов последовательности, сохраняют их в массив и сортируют. Затем вычисляют общее число пар \(m=\binom{n}{2}\) и целевой ранг \(t=\left\lfloor\frac{m+1}{2}\right\rfloor\).

После этого выполняется бинарный поиск на интервале \([0, a_{n-2}a_{n-1}]\). На каждом шаге вызывается линейный двухуказательный подсчет: левый индекс двигается слева направо, правый только уменьшается, и суммируется количество произведений, не превосходящих текущий порог. Если счет уже достиг \(t\), поиск остается в левой половине; иначе переходит в правую.

Кроме того, реализации используют целочисленные типы, достаточные для хранения и диапазона произведений, и количества пар. Благодаря этому все сравнения выполняются точно.

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

Построение последовательности требует \(O(n)\) времени, сортировка требует \(O(n\log n)\). Одна оценка \(F(x)\) работает за \(O(n)\), потому что правый указатель в течение одного прохода только убывает и не возвращается назад. Бинарный поиск выполняет \(O(\log V)\) шагов, где

$$V=a_{n-2}a_{n-1}.$$

Итоговая сложность равна

$$O(n\log n+n\log V),$$

а дополнительная память составляет \(O(n)\). На практике сортировка дает основной вклад в подготовку данных, а каждая проверка порога остается линейной.

Ссылки и литература

  1. Страница задачи: https://projecteuler.net/problem=793
  2. Порядковая статистика: Wikipedia — Order statistic
  3. Бинарный поиск: Wikipedia — Binary search algorithm
  4. Метод двух указателей: cp-algorithms — Two pointers method
  5. Модульная арифметика: Wikipedia — Modular arithmetic

ملخص المسألة

نولّد أول \(n\) حدود من المتتالية الحتمية

$$s_0=290797,\qquad s_{k+1}\equiv s_k^2 \pmod{50515093},$$

ثم نرتّب هذه القيم لنحصل على

$$a_0\le a_1\le \dots \le a_{n-1}.$$

بعد ذلك ننظر إلى كل الجداءات \(a_i a_j\) حيث \(0\le i<j<n\). عدد هذه الجداءات هو

$$m=\binom{n}{2}=\frac{n(n-1)}{2}.$$

المطلوب هو وسيط هذا المتعدد من القيم. ووفق صياغة إحصاء الرتبة المستخدمة في التطبيقات، فهذا يعني العنصر صاحب الرتبة

$$t=\left\lceil\frac{m}{2}\right\rceil=\left\lfloor\frac{m+1}{2}\right\rfloor,$$

أي أنه عندما يكون \(m\) زوجيًا نأخذ الأصغر من القيمتين الوسطيتين.

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

الفكرة الأساسية هي أننا لا نحتاج إلى توليد جميع الجداءات وعددها \(\binom{n}{2}\) صراحةً. بدلًا من ذلك نحول المسألة إلى سؤال عدٍّ عند عتبة معينة: إذا اخترنا قيمة \(x\)، فكم عدد جداءات الأزواج التي تحقق \(a_i a_j\le x\)؟ متى ما أمكن حساب هذا العدد بسرعة، صار الوسيط يُستخرج ببحث ثنائي على قيمة الجواب.

الخطوة 1: توليد المتتالية ثم ترتيبها

تُنتج العلاقة التراجعية \(n\) أعدادًا صحيحة موجبة. بعد الترتيب نحصل على

$$a_0\le a_1\le \dots \le a_{n-1}.$$

ولكل فهرس أيسر ثابت \(i\)، فإن الجداءات

$$a_i a_{i+1},\ a_i a_{i+2},\ \dots,\ a_i a_{n-1}$$

تزداد كلما تحرك الفهرس الأيمن نحو اليمين. هذه الرتابة هي البنية التي يستغلها العدّ الخطي لاحقًا.

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

لكل \(x\ge 0\) نعرّف

$$F(x)=\#\left\{(i,j):0\le i<j<n,\ a_i a_j\le x\right\}.$$

هذه الدالة رتيبة غير متناقصة. لذلك يكون الجواب المطلوب هو

$$\min\left\{x\in\mathbb{Z}_{\ge 0}:F(x)\ge t\right\}.$$

بمعنى آخر: نبحث عن أصغر عتبة تجعل عدد الجداءات التي لا تتجاوزها يصل إلى موضع الوسيط.

الخطوة 3: حساب \(F(x)\) في زمن خطي باستعمال مؤشرين

ثبّت قيمة \(x\). لكل فهرس أيسر \(i\)، ليكن \(J(i)\) أكبر فهرس يحقق \(J(i)>i\) و

$$a_i a_{J(i)}\le x.$$

عندها تكون كل الفهارس من \(i+1\) حتى \(J(i)\) صالحة أيضًا، لذا يكون إسهام الصف \(i\) مساويًا لـ

$$\max(0, J(i)-i).$$

ومن ثم

$$F(x)=\sum_{i=0}^{n-1}\max(0, J(i)-i).$$

النقطة الحاسمة أن \(J(i)\) لا يمكن أن يتحرك إلى اليمين عندما يكبر \(i\). فإذا ازداد العامل الأيسر، فلن تصبح الحدود اليمنى غير الصالحة صالحةً من جديد تحت العتبة نفسها. لذلك يكفي مؤشر أيمن واحد \(j\) يبدأ من \(n-1\) ويتحرك فقط إلى اليسار طالما أن \(a_i a_j>x\). وبما أن هذا المؤشر لا يتراجع أكثر من \(n-1\) مرة في المجموع، فإن عملية حساب \(F(x)\) كلها تكلف \(O(n)\) فقط.

الخطوة 4: إجراء بحث ثنائي على قيمة الجواب

لا بد أن يقع الجواب داخل المجال

$$0\le x\le a_{n-2}a_{n-1}.$$

فالحد الأعلى هو أكبر جداء ممكن لعنصرين بعد الترتيب. ثم نطبق بحثًا ثنائيًا من نوع lower bound على هذا المجال الصحيح. عند المنتصف \(\mathrm{mid}\) تكون قاعدة التحديث

$$F(\mathrm{mid})\ge t \Rightarrow \mathrm{hi}=\mathrm{mid},\qquad F(\mathrm{mid})<t \Rightarrow \mathrm{lo}=\mathrm{mid}+1.$$

وحين ينتهي البحث ويصبح \(\mathrm{lo}=\mathrm{hi}\)، تكون تلك القيمة هي أصغر عتبة تحقق الرتبة \(t\)، أي الوسيط المطلوب بالضبط.

الخطوة 5: مثال محلول باستخدام أول أربعة حدود

أول أربعة حدود مولّدة هي أصلًا بترتيب تصاعدي:

$$290797,\quad 629527,\quad 13339144,\quad 15552512.$$

ومن ثم

$$m=\binom{4}{2}=6,\qquad t=\left\lceil\frac{6}{2}\right\rceil=3.$$

الجداءات الستة هي

$$\begin{aligned} 290797\cdot 629527&=183064563019,\\ 290797\cdot 13339144&=3878983057768,\\ 290797\cdot 15552512&=4522623832064,\\ 629527\cdot 13339144&=8397351304888,\\ 629527\cdot 15552512&=9790726221824,\\ 13339144\cdot 15552512&=207457197129728. \end{aligned}$$

العنصر الثالث بعد الترتيب هو

$$4522623832064.$$

ومن منظور دالة العدّ أيضًا نحصل على النتيجة نفسها: عند \(x=4{,}000{,}000{,}000{,}000\) يكون \(F(x)=2<3\)، وعند \(x=4{,}600{,}000{,}000{,}000\) يصبح \(F(x)=3\ge 3\). لذلك يستقر البحث الثنائي عند

$$4522623832064.$$

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

تتبع تطبيقات C++ وPython وJava الخطوات نفسها. فهي تبدأ بتوليد أول \(n\) حد من المتتالية، ثم تخزينها في مصفوفة وترتيبها. بعد ذلك تُحسب القيمة الكلية لعدد الأزواج \(m=\binom{n}{2}\) ورتبة الهدف \(t=\left\lfloor\frac{m+1}{2}\right\rfloor\).

ثم يجري بحث ثنائي على المجال \([0, a_{n-2}a_{n-1}]\). في كل مرة يُختار فيها وسط جديد، تُستدعى عملية العدّ الخطية ذات المؤشرين: المؤشر الأيسر يتقدم من البداية، والمؤشر الأيمن لا يتحرك إلا إلى اليسار، ويُجمَع عدد الجداءات التي لا تتجاوز العتبة الحالية. إذا بلغ العدّ على الأقل \(t\)، يُحافَظ على النصف الأيسر من المجال؛ وإلا يُنتقل إلى النصف الأيمن.

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

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

توليد المتتالية يكلف \(O(n)\)، وترتيبها يكلف \(O(n\log n)\). وكل تقييم للدالة \(F(x)\) يكلف \(O(n)\)، لأن المؤشر الأيمن يتحرك إلى اليسار فقط ولا يُعاد ضبطه. أما البحث الثنائي فيجري \(O(\log V)\) خطوة حيث

$$V=a_{n-2}a_{n-1}.$$

إذًا يكون التعقيد الكلي

$$O(n\log n+n\log V),$$

بينما استهلاك الذاكرة هو \(O(n)\). عمليًا تظل كلفة الفرز هي الجزء الأكبر من التحضير، في حين يبقى اختبار العتبة في كل خطوة خطيًا.

الهوامش والمراجع

  1. صفحة المسألة: https://projecteuler.net/problem=793
  2. إحصاء الرتبة: Wikipedia — Order statistic
  3. البحث الثنائي: Wikipedia — Binary search algorithm
  4. طريقة المؤشرين: cp-algorithms — Two pointers method
  5. الحسابيات المعيارية: Wikipedia — Modular arithmetic