Let \(d(n)\) be the number of greedy steps needed to reduce \(n\) to \(0\) by repeatedly subtracting the largest perfect cube not exceeding the current value. The quantity required by the problem is
$$T(N)=\sum_{n=1}^{N-1} d(n),$$
with the final target \(N=10^{17}\). A direct simulation for every \(n<N\) is hopeless, so the solution groups numbers by the first cube removed and turns the summation into a much smaller recursive dynamic program.
For bookkeeping it is convenient to set \(d(0)=0\) and to define the partial-sum function
$$T(x)=\sum_{n=1}^{x-1} d(n)\qquad (x\ge 1).$$
The answer we want is therefore \(T(10^{17})\).
Take any \(x>1\), and let
$$m=\left\lfloor\sqrt[3]{x-1}\right\rfloor,\qquad b=m^3,\qquad r=x-b.$$
Then \(b\le x-1 < (m+1)^3\), so every integer in the interval
$$b,\ b+1,\ \dots,\ x-1$$
has the same first greedy move: subtract \(b=m^3\).
Any number in that block can be written as \(b+u\) with \(0\le u<r\), and its greedy decomposition begins with
$$b+u \longrightarrow u.$$
Therefore
$$d(b+u)=1+d(u).$$
Summing the previous identity over \(u=0,1,\dots,r-1\) gives
$$T(x)-T(b)=\sum_{u=0}^{r-1}\bigl(1+d(u)\bigr)=r+T(r).$$
Now define the cube-boundary values
$$B_m=T(m^3).$$
Then the main recurrence becomes
$$\boxed{T(x)=B_m+r+T(r),\qquad m=\left\lfloor\sqrt[3]{x-1}\right\rfloor,\ r=x-m^3.}$$
This is the central formula used by the implementations.
At an exact cube boundary, take \(x=(m+1)^3\). Then
$$r=(m+1)^3-m^3=3m^2+3m+1.$$
Substituting into the main recurrence yields
$$B_{m+1}=B_m+\bigl((m+1)^3-m^3\bigr)+T\bigl((m+1)^3-m^3\bigr).$$
So once the boundary values are computed in increasing order, each new boundary depends only on an already smaller argument. This turns the original problem into a forward sweep over consecutive cubes.
The remainder satisfies
$$1\le r\le (m+1)^3-m^3=3m^2+3m+1.$$
Since \(m\approx x^{1/3}\), we get the rough bound
$$r\le 3x^{2/3}+3x^{1/3}+1.$$
That means one recursive step reduces an argument of size about \(x\) to a remainder of size about \(x^{2/3}\). Repeating the process shrinks the problem very fast, which is why memoization is enough to make the method practical even for \(10^{17}\).
Let
$$q=\left\lfloor\sqrt[3]{N-1}\right\rfloor.$$
The algorithm first computes all boundary values
$$B_1,\ B_2,\ \dots,\ B_q$$
from left to right. Whenever \(T(x)\) is needed for a non-boundary argument, the same recurrence
$$T(x)=B_m+r+T(r)$$
is applied and the result is stored. Because many boundary gaps and remainders recur, caching turns a potentially branching recursion tree into a directed acyclic graph of already solved states.
Take \(x=10\). Then
$$m=\left\lfloor\sqrt[3]{9}\right\rfloor=2,\qquad b=2^3=8,\qquad r=10-8=2.$$
The recurrence says
$$T(10)=T(8)+2+T(2).$$
Now \(T(8)=d(1)+\cdots+d(7)=1+2+3+4+5+6+7=28\), and \(T(2)=d(1)=1\). Hence
$$T(10)=28+2+1=31.$$
This matches the direct greedy counts: \(d(8)=1\) and \(d(9)=2\), so the last two numbers contribute \(3\) on top of \(T(8)\).
The C++, Python, and Java implementations all follow the same structure. First they compute an exact floor cube root for a given integer by starting from a floating estimate and then correcting upward or downward until the surrounding cubes are in the right order. That avoids boundary mistakes near perfect cubes.
Next they build an array of cube-boundary totals \(B_m=T(m^3)\) up to \(m=\lfloor\sqrt[3]{N-1}\rfloor\). Each new entry uses the boundary recurrence, so the program only needs the gap between consecutive cubes and one recursive partial sum for that gap.
A hash table or dictionary stores every already computed value of \(T(x)\). When the same remainder appears again, the implementation returns the cached value immediately instead of recomputing the whole chain.
After the boundary sweep is complete, the program applies the same recurrence once more to the target \(N=10^{17}\). The C++ version also includes small brute-force checks for tiny inputs to confirm that the optimized recurrence matches direct simulation.
Let \(q=\lfloor\sqrt[3]{N-1}\rfloor\), and let \(M\) be the number of distinct arguments whose values are actually memoized. The deterministic preprocessing is the forward sweep over the \(q\) cube boundaries, so that part is \(O(q)\).
For any uncached argument \(x\), one recurrence step replaces \(x\) by a remainder \(r\) with size \(O(x^{2/3})\), so an individual recursion chain has depth \(O(\log\log N)\). Since each distinct state is solved only once, the total running time is \(O(M)\) cube-root evaluations plus \(O(q)\) boundary updates, and the memory usage is \(O(M+q)\). In practice \(M\) stays close to the cube-root scale and is tiny compared with \(N\).
Sei \(d(n)\) die Anzahl der Greedy-Schritte, die benötigt werden, um \(n\) durch wiederholtes Subtrahieren der größten nicht überschrittenen Kubikzahl auf \(0\) zu bringen. Gesucht ist
$$T(N)=\sum_{n=1}^{N-1} d(n),$$
für \(N=10^{17}\). Eine direkte Simulation für alle \(n<N\) ist unmöglich, daher bündelt die Lösung Zahlen danach, welche Kubikzahl im ersten Schritt entfernt wird, und reduziert die Aufgabe auf eine viel kleinere rekursive Dynamik.
Zur bequemeren Notation setzen wir \(d(0)=0\) und definieren
$$T(x)=\sum_{n=1}^{x-1} d(n)\qquad (x\ge 1).$$
Gesucht ist also \(T(10^{17})\).
Für \(x>1\) setzen wir
$$m=\left\lfloor\sqrt[3]{x-1}\right\rfloor,\qquad b=m^3,\qquad r=x-b.$$
Dann gilt \(b\le x-1 < (m+1)^3\). Folglich ist für jede Zahl im Intervall
$$b,\ b+1,\ \dots,\ x-1$$
der erste Greedy-Schritt identisch: Man zieht \(b=m^3\) ab.
Jede solche Zahl hat die Form \(b+u\) mit \(0\le u<r\), also beginnt ihre Zerlegung mit
$$b+u \longrightarrow u.$$
Daher folgt
$$d(b+u)=1+d(u).$$
Wenn man diese Identität über \(u=0,1,\dots,r-1\) aufsummiert, erhält man
$$T(x)-T(b)=\sum_{u=0}^{r-1}\bigl(1+d(u)\bigr)=r+T(r).$$
Nun definieren wir die Kubikgrenzwerte
$$B_m=T(m^3).$$
Damit lautet die Hauptrekurrenz
$$\boxed{T(x)=B_m+r+T(r),\qquad m=\left\lfloor\sqrt[3]{x-1}\right\rfloor,\ r=x-m^3.}$$
Genau diese Formel wird später im Programm verwendet.
Für einen exakten Würfelrand wählen wir \(x=(m+1)^3\). Dann ist
$$r=(m+1)^3-m^3=3m^2+3m+1.$$
Einsetzen in die Hauptrekurrenz liefert
$$B_{m+1}=B_m+\bigl((m+1)^3-m^3\bigr)+T\bigl((m+1)^3-m^3\bigr).$$
Damit kann man die Werte \(B_1,B_2,\dots\) der Reihe nach aufbauen; jeder neue Rand hängt nur von einem deutlich kleineren Argument ab.
Für den Rest gilt
$$1\le r\le (m+1)^3-m^3=3m^2+3m+1.$$
Weil \(m\approx x^{1/3}\) ist, erhält man grob
$$r\le 3x^{2/3}+3x^{1/3}+1.$$
Ein Rekursionsschritt schrumpft die Größenordnung also von etwa \(x\) auf etwa \(x^{2/3}\). Nach wenigen Wiederholungen landet man bei sehr kleinen Resten. Darum genügt Memoisierung auch für \(10^{17}\).
Sei
$$q=\left\lfloor\sqrt[3]{N-1}\right\rfloor.$$
Die Methode berechnet zuerst alle Randwerte
$$B_1,\ B_2,\ \dots,\ B_q$$
von links nach rechts. Wird ein Wert \(T(x)\) für ein Nicht-Rand-Argument benötigt, wird erneut
$$T(x)=B_m+r+T(r)$$
verwendet und das Ergebnis gespeichert. Weil viele Abstände zwischen Würfeln und viele Reste mehrfach auftreten, verwandelt das Zwischenspeichern den Rekursionsbaum effektiv in einen Graphen aus bereits gelösten Zuständen.
Nehmen wir \(x=10\). Dann
$$m=\left\lfloor\sqrt[3]{9}\right\rfloor=2,\qquad b=2^3=8,\qquad r=10-8=2.$$
Die Rekurrenz sagt
$$T(10)=T(8)+2+T(2).$$
Es ist \(T(8)=d(1)+\cdots+d(7)=1+2+3+4+5+6+7=28\) und \(T(2)=d(1)=1\). Also
$$T(10)=28+2+1=31.$$
Das stimmt mit der direkten Sicht überein: \(d(8)=1\) und \(d(9)=2\), also liefern die letzten beiden Zahlen zusammen \(3\) zusätzliche Schritte.
Die C++-, Python- und Java-Implementierungen haben dieselbe Grundstruktur. Zuerst berechnen sie eine exakte ganzzahlige Kubikwurzel, indem sie mit einer Gleitkomma-Schätzung starten und diese danach nach oben oder unten korrigieren, bis die benachbarten Kuben korrekt eingeordnet sind. Dadurch werden Rundungsfehler an perfekten Kuben vermieden.
Anschließend wird ein Array der Randwerte \(B_m=T(m^3)\) bis \(m=\lfloor\sqrt[3]{N-1}\rfloor\) aufgebaut. Jeder neue Eintrag benutzt die Randrekurrenz und benötigt daher nur die Lücke zwischen zwei aufeinanderfolgenden Kuben sowie eine rekursive Summenanfrage für genau diese Lücke.
Alle bereits berechneten Werte \(T(x)\) werden in einer Hash-Struktur gespeichert. Wenn derselbe Rest später noch einmal vorkommt, wird der gespeicherte Wert sofort zurückgegeben.
Nach dem vollständigen Randdurchlauf wird dieselbe Rekurrenz noch einmal auf \(N=10^{17}\) angewandt. Die C++-Implementierung enthält zusätzlich kleine Brute-Force-Prüfungen für Mini-Beispiele, um die schnelle Methode gegen direkte Simulation abzugleichen.
Sei \(q=\lfloor\sqrt[3]{N-1}\rfloor\), und sei \(M\) die Anzahl tatsächlich memoisierten Argumente. Der deterministische Vorlauf ist der lineare Durchgang über die \(q\) Kubikgrenzen, also \(O(q)\).
Für jedes noch nicht gespeicherte Argument \(x\) ersetzt ein Rekursionsschritt \(x\) durch einen Rest \(r\) von Größe \(O(x^{2/3})\). Eine einzelne Kette hat deshalb Tiefe \(O(\log\log N)\). Da jeder Zustand nur einmal gelöst wird, ergibt sich insgesamt \(O(M)\) Kubikwurzel-Auswertungen plus \(O(q)\) Randupdates; der Speicherbedarf ist \(O(M+q)\). In der Praxis liegt \(M\) nahe an der Kubikwurzel-Skala und ist winzig gegenüber \(N\).
\(d(n)\), her adımda mevcut değeri aşmayan en büyük küp çıkarılarak \(n\)'in \(0\)'a indirilmesi için gereken açgözlü adım sayısı olsun. Problemde istenen büyüklük
$$T(N)=\sum_{n=1}^{N-1} d(n),$$
ve hedef \(N=10^{17}\) değeridir. Tüm \(n<N\) için doğrudan simülasyon yapmak imkansız olduğundan, çözüm sayıları ilk çıkarılan küpe göre gruplar ve toplamı çok daha küçük bir özyinelemeli dinamik programa dönüştürür.
Notasyonu rahatlatmak için \(d(0)=0\) kabul edelim ve
$$T(x)=\sum_{n=1}^{x-1} d(n)\qquad (x\ge 1)$$
tanımını yapalım. Aradığımız cevap böylece \(T(10^{17})\) olur.
Her \(x>1\) için
$$m=\left\lfloor\sqrt[3]{x-1}\right\rfloor,\qquad b=m^3,\qquad r=x-b$$
alalım. O zaman \(b\le x-1 < (m+1)^3\) olduğundan
$$b,\ b+1,\ \dots,\ x-1$$
aralığındaki her sayı aynı ilk açgözlü hamleyi yapar: \(b=m^3\) çıkarılır.
Bu bloktaki her sayı \(0\le u<r\) için \(b+u\) biçimindedir ve ayrışımı
$$b+u \longrightarrow u$$
ile başlar. Dolayısıyla
$$d(b+u)=1+d(u)$$
elde edilir.
Bu eşitliği \(u=0,1,\dots,r-1\) üzerinde toplarsak
$$T(x)-T(b)=\sum_{u=0}^{r-1}\bigl(1+d(u)\bigr)=r+T(r)$$
olur. Şimdi küp sınırı değerlerini
$$B_m=T(m^3)$$
diye tanımlayalım. Böylece temel özyineleme
$$\boxed{T(x)=B_m+r+T(r),\qquad m=\left\lfloor\sqrt[3]{x-1}\right\rfloor,\ r=x-m^3}$$
şeklini alır. Uygulamanın dayandığı ana formül budur.
Eğer \(x=(m+1)^3\) seçilirse
$$r=(m+1)^3-m^3=3m^2+3m+1$$
olur. Bunu ana formüle koyunca
$$B_{m+1}=B_m+\bigl((m+1)^3-m^3\bigr)+T\bigl((m+1)^3-m^3\bigr)$$
elde edilir. Böylece ardışık küpler üzerinde soldan sağa ilerleyen bir dinamik program kurulur; her yeni sınır yalnızca daha küçük bir argümana ihtiyaç duyar.
Kalan için
$$1\le r\le (m+1)^3-m^3=3m^2+3m+1$$
geçerlidir. \(m\approx x^{1/3}\) olduğundan kaba üst sınır
$$r\le 3x^{2/3}+3x^{1/3}+1$$
olur. Yani tek bir özyineleme adımı, yaklaşık \(x\) boyutundaki bir problemi yaklaşık \(x^{2/3}\) boyutuna indirir. Bu hızlı küçülme sayesinde memoization ile yöntem \(10^{17}\) için de rahatça çalışır.
Şimdi
$$q=\left\lfloor\sqrt[3]{N-1}\right\rfloor$$
olsun. Algoritma önce
$$B_1,\ B_2,\ \dots,\ B_q$$
değerlerini soldan sağa hesaplar. Küp sınırı olmayan bir \(T(x)\) gerektiğinde yine
$$T(x)=B_m+r+T(r)$$
uygulanır ve bulunan sonuç saklanır. Aynı boşluklar ve aynı kalanlar tekrar tekrar ortaya çıktığı için önbellekleme özyineleme ağacını çözülen durumlardan oluşan yönlü çevrimsiz bir yapıya dönüştürür.
\(x=10\) alalım. O zaman
$$m=\left\lfloor\sqrt[3]{9}\right\rfloor=2,\qquad b=2^3=8,\qquad r=10-8=2$$
olur. Formül
$$T(10)=T(8)+2+T(2)$$
der. Burada \(T(8)=d(1)+\cdots+d(7)=1+2+3+4+5+6+7=28\) ve \(T(2)=d(1)=1\) olduğundan
$$T(10)=28+2+1=31$$
çıkar. Bu sonuç doğrudan sayımla da aynıdır: \(d(8)=1\) ve \(d(9)=2\), yani son iki sayı toplam \(3\) katkı yapar.
C++, Python ve Java uygulamaları aynı iskeleti izler. Önce kayan noktalı bir küpkök tahmini alınır; sonra çevredeki küpler karşılaştırılarak değer yukarı veya aşağı düzeltilir. Böylece tam küp sınırlarında yuvarlama hatası kalmaz.
Daha sonra \(m=\lfloor\sqrt[3]{N-1}\rfloor\) değerine kadar tüm küp sınırı toplamlarını içeren bir dizi oluşturulur. Her yeni sınır değeri, ardışık iki küp arasındaki fark ve bu fark için gereken özyinelemeli toplam kullanılarak hesaplanır.
Önceden hesaplanmış her \(T(x)\) değeri bir hash tablosu ya da sözlük içinde saklanır. Aynı kalan daha sonra yeniden istenirse sonuç doğrudan önbellekten alınır.
Sınır taraması tamamlandıktan sonra aynı özyineleme hedef \(N=10^{17}\) için bir kez daha uygulanır. C++ sürümü ayrıca küçük girdilerde brute-force karşılaştırmaları yaparak hızlı yöntemin doğruluğunu kontrol eder.
\(q=\lfloor\sqrt[3]{N-1}\rfloor\) ve \(M\) de gerçekten önbelleğe alınan farklı argüman sayısı olsun. Deterministik ön işlem, \(q\) tane küp sınırı üzerinden tek yönlü geçiştir; dolayısıyla bu kısım \(O(q)\) zaman alır.
Önbellekte olmayan bir \(x\) için tek adımda kalan \(r\), \(O(x^{2/3})\) boyutuna düşer. Bu yüzden tek bir özyineleme zincirinin derinliği \(O(\log\log N)\) olur. Her farklı durum yalnızca bir kez çözüldüğünden toplam çalışma maliyeti \(O(M)\) adet küpkök değerlendirmesi artı \(O(q)\) sınır güncellemesidir; bellek kullanımı ise \(O(M+q)\)'dur. Pratikte \(M\), küpkök ölçeğinde kalır ve \(N\)'ye göre çok küçüktür.
Sea \(d(n)\) el número de pasos codiciosos necesarios para reducir \(n\) hasta \(0\) restando siempre el mayor cubo perfecto que no supera al valor actual. La cantidad pedida es
$$T(N)=\sum_{n=1}^{N-1} d(n),$$
con objetivo final \(N=10^{17}\). Simular todos los \(n<N\) es inviable, así que la solución agrupa los enteros según el primer cubo que se elimina y convierte la suma en un programa dinámico recursivo mucho más pequeño.
Para simplificar la notación tomamos \(d(0)=0\) y definimos
$$T(x)=\sum_{n=1}^{x-1} d(n)\qquad (x\ge 1).$$
Por tanto, la respuesta buscada es \(T(10^{17})\).
Fijemos \(x>1\) y pongamos
$$m=\left\lfloor\sqrt[3]{x-1}\right\rfloor,\qquad b=m^3,\qquad r=x-b.$$
Entonces \(b\le x-1 < (m+1)^3\), de modo que todos los enteros del intervalo
$$b,\ b+1,\ \dots,\ x-1$$
comparten el mismo primer movimiento codicioso: restar \(b=m^3\).
Cada número del bloque se escribe como \(b+u\) con \(0\le u<r\), y su descomposición empieza con
$$b+u \longrightarrow u.$$
Así obtenemos
$$d(b+u)=1+d(u).$$
Al sumar la identidad anterior sobre \(u=0,1,\dots,r-1\) se obtiene
$$T(x)-T(b)=\sum_{u=0}^{r-1}\bigl(1+d(u)\bigr)=r+T(r).$$
Introduzcamos ahora los valores en frontera cúbica
$$B_m=T(m^3).$$
La recurrencia principal queda
$$\boxed{T(x)=B_m+r+T(r),\qquad m=\left\lfloor\sqrt[3]{x-1}\right\rfloor,\ r=x-m^3.}$$
Esa es la identidad central que explotan las implementaciones.
Si tomamos \(x=(m+1)^3\), entonces
$$r=(m+1)^3-m^3=3m^2+3m+1.$$
Sustituyendo en la fórmula principal se llega a
$$B_{m+1}=B_m+\bigl((m+1)^3-m^3\bigr)+T\bigl((m+1)^3-m^3\bigr).$$
Esto permite construir los valores \(B_1,B_2,\dots\) en orden creciente: cada nueva frontera depende solo de un argumento mucho menor.
El residuo cumple
$$1\le r\le (m+1)^3-m^3=3m^2+3m+1.$$
Como \(m\approx x^{1/3}\), resulta la cota aproximada
$$r\le 3x^{2/3}+3x^{1/3}+1.$$
Es decir, una sola llamada recursiva transforma un argumento de tamaño cercano a \(x\) en otro de tamaño cercano a \(x^{2/3}\). Tras pocas iteraciones el problema cae a valores muy pequeños, y por eso la memorización basta incluso para \(10^{17}\).
Sea
$$q=\left\lfloor\sqrt[3]{N-1}\right\rfloor.$$
El algoritmo calcula primero todos los valores de frontera
$$B_1,\ B_2,\ \dots,\ B_q$$
de izquierda a derecha. Cuando se necesita un valor \(T(x)\) que no está en frontera, se vuelve a aplicar
$$T(x)=B_m+r+T(r)$$
y el resultado se guarda. Como muchas diferencias entre cubos consecutivos y muchos residuos reaparecen, la caché convierte el árbol recursivo en un grafo acíclico de estados ya resueltos.
Tomemos \(x=10\). Entonces
$$m=\left\lfloor\sqrt[3]{9}\right\rfloor=2,\qquad b=2^3=8,\qquad r=10-8=2.$$
La recurrencia da
$$T(10)=T(8)+2+T(2).$$
Ahora bien, \(T(8)=d(1)+\cdots+d(7)=1+2+3+4+5+6+7=28\), y \(T(2)=d(1)=1\). Por tanto,
$$T(10)=28+2+1=31.$$
Esto coincide con el conteo directo: \(d(8)=1\) y \(d(9)=2\), así que los dos últimos números añaden \(3\) al total acumulado hasta \(8\).
Las implementaciones en C++, Python y Java siguen exactamente la misma estrategia. Primero calculan una raíz cúbica entera exacta partiendo de una aproximación en coma flotante y corrigiéndola después hacia arriba o hacia abajo hasta que los cubos vecinos quedan bien situados. Eso evita errores en torno a los cubos perfectos.
Después construyen un arreglo de valores de frontera \(B_m=T(m^3)\) hasta \(m=\lfloor\sqrt[3]{N-1}\rfloor\). Cada nueva entrada se obtiene mediante la recurrencia de frontera, usando la diferencia entre dos cubos consecutivos y una única consulta recursiva para esa diferencia.
Todos los valores ya calculados de \(T(x)\) se guardan en una tabla hash o diccionario. Si un mismo residuo vuelve a aparecer, la implementación reutiliza el valor almacenado inmediatamente.
Una vez completado el barrido de fronteras, la misma recurrencia se aplica a \(N=10^{17}\). La versión en C++ además compara varios casos pequeños con fuerza bruta para verificar que la formulación rápida coincide con la simulación directa.
Sea \(q=\lfloor\sqrt[3]{N-1}\rfloor\) y sea \(M\) el número de argumentos distintos que realmente se memorizan. El preprocesamiento determinista es el barrido lineal sobre las \(q\) fronteras cúbicas, así que esa parte cuesta \(O(q)\).
Para un argumento no cacheado \(x\), un paso de la recurrencia lo reemplaza por un residuo \(r\) de tamaño \(O(x^{2/3})\). En consecuencia, una cadena recursiva individual tiene profundidad \(O(\log\log N)\). Como cada estado se resuelve una sola vez, el tiempo total es \(O(M)\) evaluaciones de raíz cúbica más \(O(q)\) actualizaciones de frontera, y la memoria es \(O(M+q)\). En la práctica \(M\) se mantiene cerca de la escala cúbica y es diminuto frente a \(N\).
设 \(d(n)\) 表示把 \(n\) 通过如下贪心规则降到 \(0\) 所需的步数:每一步都减去不超过当前值的最大完全立方数。题目要求计算
$$T(N)=\sum_{n=1}^{N-1} d(n),$$
其中最终目标是 \(N=10^{17}\)。如果对每个 \(n<N\) 单独模拟,代价会大得不可接受,所以关键是把许多整数按“第一次减去哪个立方数”分组,再对整组一起求和。
为了让递推写得更自然,我们先约定 \(d(0)=0\),并定义前缀求和函数
$$T(x)=\sum_{n=1}^{x-1} d(n)\qquad (x\ge 1).$$
这样一来,题目的答案就是 \(T(10^{17})\)。
任取 \(x>1\),令
$$m=\left\lfloor\sqrt[3]{x-1}\right\rfloor,\qquad b=m^3,\qquad r=x-b.$$
因为 \(b\le x-1 < (m+1)^3\),所以区间
$$b,\ b+1,\ \dots,\ x-1$$
中的每个整数,第一次贪心操作都会减去同一个立方数 \(b=m^3\)。
区间里的任意数都可写成 \(b+u\),其中 \(0\le u<r\)。它的第一步一定是
$$b+u \longrightarrow u,$$
因此有
$$d(b+u)=1+d(u).$$
这一步把“从 \(b+u\) 开始的问题”拆成了“一次固定操作”加上“从较小余数 \(u\) 开始的问题”。
把上式对 \(u=0,1,\dots,r-1\) 求和,就得到
$$T(x)-T(b)=\sum_{u=0}^{r-1}\bigl(1+d(u)\bigr)=r+T(r).$$
现在定义立方边界上的总和
$$B_m=T(m^3).$$
于是主递推写成
$$\boxed{T(x)=B_m+r+T(r),\qquad m=\left\lfloor\sqrt[3]{x-1}\right\rfloor,\ r=x-m^3.}$$
这个公式的意义非常直接:先加上所有小于 \(m^3\) 的贡献,再加上当前块里每个数都会多出来的那一次减法,最后再补上这些余数本身继续递归时产生的总贡献。
如果把 \(x\) 取成下一个整立方,即 \(x=(m+1)^3\),那么余数恰好是相邻两个立方之间的间隔
$$r=(m+1)^3-m^3=3m^2+3m+1.$$
代回主递推可得
$$B_{m+1}=B_m+\bigl((m+1)^3-m^3\bigr)+T\bigl((m+1)^3-m^3\bigr).$$
这说明只要按 \(m=1,2,3,\dots\) 的顺序向前推进,就能逐个算出所有立方边界值。每前进一步,需要处理的只是一个远小于 \((m+1)^3\) 的差值。
由定义可知余数满足
$$1\le r\le (m+1)^3-m^3=3m^2+3m+1.$$
而 \(m\) 大约是 \(x^{1/3}\),因此得到粗略上界
$$r\le 3x^{2/3}+3x^{1/3}+1.$$
也就是说,一次递归会把规模大约为 \(x\) 的问题降到规模大约为 \(x^{2/3}\) 的问题。继续递归时,指数会从 \(1\) 变成 \(2/3\)、再变成 \((2/3)^2\)、\((2/3)^3\) 等,很快就降到很小的范围。这正是为什么题目虽然目标是 \(10^{17}\),但状态数量仍然非常可控。
记
$$q=\left\lfloor\sqrt[3]{N-1}\right\rfloor.$$
算法首先从左到右计算所有边界值
$$B_1,\ B_2,\ \dots,\ B_q.$$
如果在这个过程中需要某个非边界位置的 \(T(x)\),就继续使用同样的公式
$$T(x)=B_m+r+T(r)$$
并把求出的结果缓存起来。由于不同立方间隔和递归余数之间会出现大量重叠,一旦某个状态已经算过,后面再次遇到时就可以直接复用。这样,原本可能分叉的递归树会被压缩成一个只含已知状态的有向无环图。
看一个小例子:取 \(x=10\)。此时
$$m=\left\lfloor\sqrt[3]{9}\right\rfloor=2,\qquad b=2^3=8,\qquad r=10-8=2.$$
主递推给出
$$T(10)=T(8)+2+T(2).$$
而
$$T(8)=d(1)+d(2)+\cdots+d(7)=1+2+3+4+5+6+7=28,$$
并且 \(T(2)=d(1)=1\)。所以
$$T(10)=28+2+1=31.$$
这和直接观察完全一致:\(8\) 只需减一次,\(9\) 先减 \(8\) 再减 \(1\),所以最后两个数额外贡献了 \(1+2=3\)。
C++、Python 和 Java 三个实现采用的是同一套思路。首先,它们都会先用浮点立方根得到一个初值,然后通过比较相邻立方数向上或向下微调,最终得到精确的整数下取整立方根。这样可以避免在完美立方附近因为浮点误差而选错块。
接着,程序建立一个数组,用来保存所有立方边界上的总和 \(B_m=T(m^3)\)。这些值按顺序推进,每次只需要知道相邻两个立方之间的差,以及这个差值对应的递归总和。
所有已经求出的 \(T(x)\) 都会被放入哈希表或字典中。之后如果同一个余数再次出现,程序就直接返回缓存值,不再重复展开整条递归链。
当所有边界值都准备好以后,程序再把同样的递推应用到最终目标 \(N=10^{17}\) 上。C++ 实现还额外保留了一些小范围的暴力校验,用来确认快速方法和直接模拟在小样例上完全一致。
设 \(q=\lfloor\sqrt[3]{N-1}\rfloor\),再设 \(M\) 为记忆化过程中真正出现并被缓存的不同参数个数。确定性的预处理就是从左到右扫过这 \(q\) 个立方边界,因此这一部分是 \(O(q)\)。
对任意一个尚未缓存的参数 \(x\),一次递推会把它变成大小为 \(O(x^{2/3})\) 的余数,所以单条递归链的深度是 \(O(\log\log N)\)。由于每个状态最多只求一次,总时间可以写成 \(O(M)\) 次立方根计算再加上 \(O(q)\) 次边界更新,而空间复杂度是 \(O(M+q)\)。实际运行时,\(M\) 会停留在立方根量级,和 \(N\) 相比非常小。
Пусть \(d(n)\) обозначает число жадных шагов, необходимых для сведения \(n\) к \(0\), если на каждом шаге вычитать наибольший полный куб, не превосходящий текущее значение. Требуется вычислить
$$T(N)=\sum_{n=1}^{N-1} d(n),$$
где в задаче нужно взять \(N=10^{17}\). Полная симуляция для всех \(n<N\) невозможна, поэтому решение группирует числа по тому, какой куб снимается первым, и превращает огромную сумму в сравнительно компактную рекурсивную динамику.
Для удобства положим \(d(0)=0\) и введем функцию частичных сумм
$$T(x)=\sum_{n=1}^{x-1} d(n)\qquad (x\ge 1).$$
Тогда искомый ответ есть \(T(10^{17})\).
Возьмем произвольное \(x>1\) и положим
$$m=\left\lfloor\sqrt[3]{x-1}\right\rfloor,\qquad b=m^3,\qquad r=x-b.$$
Из неравенства \(b\le x-1 < (m+1)^3\) следует, что каждое число из диапазона
$$b,\ b+1,\ \dots,\ x-1$$
в первом жадном шаге вычитает один и тот же куб \(b=m^3\).
Любое такое число имеет вид \(b+u\), где \(0\le u<r\), и его начало разложения таково:
$$b+u \longrightarrow u.$$
Следовательно,
$$d(b+u)=1+d(u).$$
Суммируя предыдущее тождество по \(u=0,1,\dots,r-1\), получаем
$$T(x)-T(b)=\sum_{u=0}^{r-1}\bigl(1+d(u)\bigr)=r+T(r).$$
Введем теперь значения на кубических границах
$$B_m=T(m^3).$$
Тогда основная рекурсия принимает вид
$$\boxed{T(x)=B_m+r+T(r),\qquad m=\left\lfloor\sqrt[3]{x-1}\right\rfloor,\ r=x-m^3.}$$
Именно эта формула лежит в основе всех трех реализаций.
Если выбрать \(x=(m+1)^3\), то остаток равен
$$r=(m+1)^3-m^3=3m^2+3m+1.$$
Подстановка в основную рекурсию дает
$$B_{m+1}=B_m+\bigl((m+1)^3-m^3\bigr)+T\bigl((m+1)^3-m^3\bigr).$$
Отсюда видно, что значения на границах можно строить последовательно: каждый следующий куб зависит только от существенно меньшего аргумента.
Для остатка выполняется оценка
$$1\le r\le (m+1)^3-m^3=3m^2+3m+1.$$
Так как \(m\) имеет порядок \(x^{1/3}\), получаем грубую верхнюю границу
$$r\le 3x^{2/3}+3x^{1/3}+1.$$
Значит, один рекурсивный шаг уменьшает аргумент примерно с масштаба \(x\) до масштаба \(x^{2/3}\). После нескольких повторений размер становится очень маленьким. Именно поэтому мемоизация делает метод практичным даже при \(N=10^{17}\).
Пусть
$$q=\left\lfloor\sqrt[3]{N-1}\right\rfloor.$$
Сначала алгоритм последовательно вычисляет все граничные значения
$$B_1,\ B_2,\ \dots,\ B_q.$$
Если по пути требуется значение \(T(x)\) для точки, не являющейся кубической границей, снова применяется формула
$$T(x)=B_m+r+T(r),$$
а найденный результат сохраняется. Поскольку многие промежутки между соседними кубами и многие остатки повторяются, кэш устраняет повторные вычисления и сжимает рекурсивное дерево в граф уже решенных состояний.
Рассмотрим \(x=10\). Тогда
$$m=\left\lfloor\sqrt[3]{9}\right\rfloor=2,\qquad b=2^3=8,\qquad r=10-8=2.$$
Основная формула дает
$$T(10)=T(8)+2+T(2).$$
Здесь \(T(8)=d(1)+\cdots+d(7)=1+2+3+4+5+6+7=28\), а \(T(2)=d(1)=1\). Следовательно,
$$T(10)=28+2+1=31.$$
Это полностью совпадает с прямым подсчетом: \(d(8)=1\), а \(d(9)=2\), значит два последних числа добавляют ровно \(3\) к сумме до \(8\).
Реализации на C++, Python и Java устроены одинаково. Сначала они получают точный целочисленный кубический корень: берут плавающее приближение, а затем подправляют его вверх или вниз, пока соседние кубы не окажутся по правильные стороны от аргумента. Это снимает риск ошибок на границах полных кубов.
Затем строится массив граничных сумм \(B_m=T(m^3)\) вплоть до \(m=\lfloor\sqrt[3]{N-1}\rfloor\). Каждый новый элемент вычисляется по граничной рекурсии, поэтому на шаге нужны только разность соседних кубов и одно рекурсивное значение для этой разности.
Все уже найденные значения \(T(x)\) записываются в хеш-таблицу. Если позже тот же остаток встретится снова, программа просто вернет сохраненный результат.
После завершения прохода по границам та же рекурсия применяется к конечной цели \(N=10^{17}\). В C++-версии добавлены и маленькие проверки грубой силой на небольших входах, чтобы сверить быструю схему с прямой симуляцией.
Пусть \(q=\lfloor\sqrt[3]{N-1}\rfloor\), а \(M\) обозначает число различных аргументов, реально попавших в кэш. Полностью определенная часть алгоритма, то есть проход по \(q\) кубическим границам, стоит \(O(q)\).
Для любого невычисленного ранее аргумента \(x\) один шаг рекурсии заменяет его остатком размера \(O(x^{2/3})\), поэтому глубина отдельной цепочки составляет \(O(\log\log N)\). Так как каждое уникальное состояние решается только один раз, общая трудоемкость равна \(O(M)\) вычислений кубического корня плюс \(O(q)\) граничных обновлений, а память равна \(O(M+q)\). На практике \(M\) остается порядка кубического корня и ничтожно мало по сравнению с \(N\).
لتكن \(d(n)\) هي عدد الخطوات الجشعة اللازمة لاختزال \(n\) إلى \(0\) عندما نطرح في كل مرة أكبر مكعب كامل لا يتجاوز القيمة الحالية. المطلوب في المسألة هو حساب
$$T(N)=\sum_{n=1}^{N-1} d(n),$$
مع الهدف النهائي \(N=10^{17}\). من المستحيل عمليًا محاكاة جميع القيم \(n<N\) واحدةً واحدة، لذلك تجمع الفكرة الرياضية الأعداد بحسب المكعب الذي يُطرح في الخطوة الأولى، ثم تحوّل الجمع الضخم إلى برنامج ديناميكي استدعائي صغير نسبيًا.
لتسهيل الصياغة نضع اصطلاحًا \(d(0)=0\)، ثم نعرّف دالة المجاميع الجزئية
$$T(x)=\sum_{n=1}^{x-1} d(n)\qquad (x\ge 1).$$
وعندئذ تصبح الإجابة المطلوبة هي \(T(10^{17})\).
خذ أي \(x>1\)، وعرّف
$$m=\left\lfloor\sqrt[3]{x-1}\right\rfloor,\qquad b=m^3,\qquad r=x-b.$$
بما أن \(b\le x-1 < (m+1)^3\)، فإن كل عدد في المجال
$$b,\ b+1,\ \dots,\ x-1$$
يبدأ بالخطوة الجشعة نفسها تمامًا: طرح \(b=m^3\).
ويمكن كتابة أي عدد في هذه الكتلة على الصورة \(b+u\) حيث \(0\le u<r\)، فتبدأ عمليته بـ
$$b+u \longrightarrow u.$$
ومن ثم
$$d(b+u)=1+d(u).$$
إذا جمعنا العلاقة السابقة على \(u=0,1,\dots,r-1\)، نحصل على
$$T(x)-T(b)=\sum_{u=0}^{r-1}\bigl(1+d(u)\bigr)=r+T(r).$$
ولنعرّف الآن قيم الحدود المكعبة بواسطة
$$B_m=T(m^3).$$
فتصبح العلاقة الأساسية
$$\boxed{T(x)=B_m+r+T(r),\qquad m=\left\lfloor\sqrt[3]{x-1}\right\rfloor,\ r=x-m^3.}$$
وهذه هي الصيغة الجوهرية التي تعتمد عليها جميع التطبيقات.
إذا أخذنا \(x=(m+1)^3\)، فإن الباقي يساوي الفجوة بين مكعبين متتاليين:
$$r=(m+1)^3-m^3=3m^2+3m+1.$$
وبالتعويض في العلاقة الأساسية نحصل على
$$B_{m+1}=B_m+\bigl((m+1)^3-m^3\bigr)+T\bigl((m+1)^3-m^3\bigr).$$
وهذا يعني أن قيم الحدود \(B_1,B_2,\dots\) يمكن بناؤها من اليسار إلى اليمين، لأن كل حد جديد يحتاج فقط إلى وسيط أصغر بكثير من المكعب الجديد نفسه.
لدينا دائمًا
$$1\le r\le (m+1)^3-m^3=3m^2+3m+1.$$
وبما أن \(m\) من رتبة \(x^{1/3}\)، ينتج التقدير التقريبي
$$r\le 3x^{2/3}+3x^{1/3}+1.$$
أي إن خطوة استدعائية واحدة تخفّض وسيطًا بحجم يقارب \(x\) إلى وسيط بحجم يقارب \(x^{2/3}\). وبعد تكرار العملية عدة مرات يصبح الحجم صغيرًا جدًا، ولهذا تكفي الذاكرة المؤقتة لجعل الحساب عمليًا حتى عند \(10^{17}\).
لنكتب
$$q=\left\lfloor\sqrt[3]{N-1}\right\rfloor.$$
تبدأ الخوارزمية بحساب جميع قيم الحدود
$$B_1,\ B_2,\ \dots,\ B_q$$
بالترتيب. وعندما تحتاج إلى قيمة \(T(x)\) لا تقع على حد مكعّبي، فإنها تعيد تطبيق العلاقة
$$T(x)=B_m+r+T(r)$$
ثم تحفظ النتيجة. وبسبب تكرار كثير من الفجوات بين المكعبات وكثير من البواقي، فإن التخزين المؤقت يضغط شجرة الاستدعاءات إلى رسم بياني لا دوري من حالات سبق حلّها.
لنأخذ \(x=10\). عندئذ
$$m=\left\lfloor\sqrt[3]{9}\right\rfloor=2,\qquad b=2^3=8,\qquad r=10-8=2.$$
وتعطينا العلاقة الأساسية
$$T(10)=T(8)+2+T(2).$$
نحسب أن \(T(8)=d(1)+\cdots+d(7)=1+2+3+4+5+6+7=28\)، وكذلك \(T(2)=d(1)=1\). إذن
$$T(10)=28+2+1=31.$$
وهذا يطابق العد المباشر تمامًا: فالقيمة \(8\) تحتاج إلى خطوة واحدة، والقيمة \(9\) تحتاج إلى خطوتين، لذا تضيف آخر قيمتين مقدار \(3\) فوق المجموع حتى \(8\).
تتبع تطبيقات C++ وPython وJava البنية نفسها. أولًا تُحسب الجذر التكعيبي الصحيح بالانطلاق من تقدير عشري، ثم تُصحَّح القيمة صعودًا أو هبوطًا حتى تقع المكعبات المجاورة في الموضع الصحيح. بهذه الطريقة لا تبقى أخطاء تقريب قرب المكعبات الكاملة.
بعد ذلك تُبنى مصفوفة لقيم الحدود المكعبة \(B_m=T(m^3)\) حتى \(m=\lfloor\sqrt[3]{N-1}\rfloor\). وكل حد جديد يُحسب باستعمال علاقة الحدود، أي من فرق مكعبين متتاليين ومن قيمة مجموع جزئي واحدة مرتبطة بهذا الفرق.
كل قيمة محسوبة سابقًا من \(T(x)\) تُخزَّن في قاموس أو جدول تجزئة. فإذا ظهر الباقي نفسه مرة أخرى، تسترجع الخوارزمية النتيجة المحفوظة فورًا بدل إعادة الحساب.
بعد اكتمال المرور على الحدود، تُطبَّق العلاقة نفسها مرة أخيرة على \(N=10^{17}\). كما تتضمن نسخة C++ اختبارات صغيرة بالمحاكاة المباشرة للتأكد من أن الطريقة السريعة تطابق العد الخام على المدخلات الصغيرة.
ليكن \(q=\lfloor\sqrt[3]{N-1}\rfloor\)، وليكن \(M\) عدد الوسائط المختلفة التي تُحفظ فعليًا في الذاكرة المؤقتة. الجزء الحتمي من العمل هو المرور الأمامي على حدود المكعبات وعددها \(q\)، ولذلك تكلفته \(O(q)\).
أما لأي وسيط غير مخزَّن \(x\)، فإن خطوة واحدة من العلاقة تستبدله بباقٍ حجمه \(O(x^{2/3})\)، ولذلك يكون عمق سلسلة الاستدعاء الواحدة \(O(\log\log N)\). وبما أن كل حالة مميزة تُحل مرة واحدة فقط، فالزمن الكلي هو \(O(M)\) عمليات جذر تكعيبي بالإضافة إلى \(O(q)\) تحديثات حدودية، في حين أن الذاكرة المستعملة هي \(O(M+q)\). وعمليًا يبقى \(M\) قريبًا من مقياس الجذر التكعيبي، أي صغيرًا جدًا مقارنةً بـ \(N\).