688 Piles of Plates.pl 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. #!/usr/bin/perl
  2. # Author: Daniel "Trizen" Șuteu
  3. # Date: 15 November 2019
  4. # https://github.com/trizen
  5. # Solution to the "Piles of Plates" problem.
  6. # https://projecteuler.net/problem=688
  7. # Let:
  8. # g(k) = k mod 2
  9. # f(k) = Sum_{d|k} g(d) = Sum_{d|k, d is odd} 1
  10. # Then:
  11. # F(n) = Sum_{k=1..n} f(k)
  12. # F(n) = Sum_{k=1..n} g(k) * floor(n/k)
  13. # S(n) = Sum_{k=1..n} F(k)
  14. # S(n) = Sum_{k=1..n} Sum_{j=1..k} f(j)
  15. # S(n) = Sum_{k=1..n} f(k) * (n - k + 1)
  16. # By splitting the last sum into two sums, we get:
  17. # S(n) = (n+1)*Sum_{k=1..n} f(k) - Sum_{k=1..n} f(k)*k
  18. # In order to compute S(10^16), we need a sub-linear formula for computing:
  19. # A(n) = Sum_{k=1..n} f(k)
  20. # B(n) = Sum_{k=1..n} k*f(k)
  21. # Then:
  22. # S(n) = (n+1)*A(n) - B(n)
  23. # OEIS sequences:
  24. # A(n) = A060831(n)
  25. # B(n) = A285900(n)
  26. # Formulas:
  27. # A(n) = Sum_{k=1..floor((n+1)/2)} floor(n/(2*k-1))
  28. # B(n) = Sum_{k=1..floor((n+1)/2)} (2*k-1)/2 * floor(n/(2*k-1)) * floor(1 + n/(2*k-1))
  29. # A(n) can be computed in sub-linear time as:
  30. # A(n) = H(n) - H(floor(n/2))
  31. # where:
  32. # H(n) = Sum_{k=1..n} floor(n/k)
  33. # H(n) = 2*Sum_{k=1..floor(sqrt(n))} floor(n/k) - floor(sqrt(n))^2
  34. # Alternatively:
  35. # A(n) = Sum_{k=1..floor(sqrt(n))} (V(floor(n/k)) + g(k)*floor(n/k)) - V(floor(sqrt(n)))*floor(sqrt(n))
  36. # where:
  37. # V(n) = floor((n+1)/2)
  38. # B(n) can be computed in sub-linear time as:
  39. # B(n) = Sum_{k=1..floor(sqrt(n))} k * (G(floor(n/k)) + g(k) * F_1(floor(n/k))) - F_1(floor(sqrt(n))) * G(floor(sqrt(n)))
  40. # where:
  41. # G(n) = floor((n+1)/2)^2
  42. # F_1(n) = n*(n+1)/2
  43. # Runtime: 1 min, 21 sec.
  44. use 5.020;
  45. use strict;
  46. use warnings;
  47. use integer;
  48. use ntheory qw(:all);
  49. use experimental qw(signatures);
  50. my $mod = 1000000007;
  51. sub G($n) {
  52. my $t = ($n + 1) >> 1;
  53. mulmod($t, $t, $mod);
  54. }
  55. sub H($n) {
  56. my $A = 0;
  57. my $s = sqrtint($n);
  58. foreach my $k (1 .. $s) {
  59. $A += int($n / $k);
  60. $A %= $mod;
  61. }
  62. $A = mulmod($A, 2, $mod);
  63. $A = submod($A, mulmod($s, $s, $mod), $mod);
  64. return $A;
  65. }
  66. sub A($n) {
  67. submod(H($n), H($n >> 1), $mod);
  68. }
  69. sub B($n) {
  70. my $s = sqrtint($n);
  71. my $A = 0;
  72. foreach my $k (1 .. $s) {
  73. my $t = int($n / $k);
  74. $A += ($k * G($t)) % $mod;
  75. $A %= $mod;
  76. }
  77. my $B = 0;
  78. for (my $k = 1 ; $k <= $s ; $k += 2) {
  79. my $t = int($n / $k);
  80. $B += ($k * mulmod($t, $t + 1, $mod)) % $mod;
  81. $B %= $mod;
  82. }
  83. $B = divmod($B, 2, $mod);
  84. my $C = divmod(mulmod(G($s), mulmod($s, $s + 1, $mod), $mod), 2, $mod);
  85. my $total = 0; # A + B - C
  86. $total = addmod($A, $B, $mod);
  87. $total = submod($total, $C, $mod);
  88. return $total;
  89. }
  90. sub S($n) {
  91. submod(mulmod($n + 1, A($n), $mod), B($n), $mod);
  92. }
  93. S(100) == 12656 or die "error";
  94. foreach my $n (1..16) {
  95. say "S(10^$n) = ", S(powint(10, $n));
  96. }
  97. __END__
  98. S(10^1) = 83
  99. S(10^2) = 12656
  100. S(10^3) = 1817711
  101. S(10^4) = 238998218
  102. S(10^5) = 29651877833
  103. S(10^6) = 3540779596783
  104. S(10^7) = 411641938861705
  105. S(10^8) = 46920649099203041
  106. S(10^9) = 5267711097612683319
  107. S(10^10) = 584335736126953554014
  108. S(10^11) = 64190036334552593839325
  109. S(10^12) = 6994649906587129113148380
  110. S(10^13) = 757029617982294029316779201
  111. S(10^14) = 81459424530700780705752580583