printРешение

Тема: перебор, решето для поиска простых чисел
Сложность: выше среднего

Лобовое решение выглядит так:
var s,t,k,n,i:integer;
begin
  read(s);
  for n:=4 to sqr(s-1) do
  begin
    k:=0;
    t:=0;
    for i:=n div 2 downto 1 do
      if n mod i=0 then
      begin
        inc(k);
        t:=t+i;
        if k=2 then
          break;
      end;
    if (k=2) and (t=s) then
      writeln(n);
  end;
end.
В качестве кандидатов нужно рассматривать числа, не превышающие `(S-1)^2`, так как в худшем случае составное число вида `x^2` будет иметь два наибольших делителя `x` и 1. Время работы этого алгоритма `O(S^4)`. Можно существенно ускорить вычисления, если учесть, что при поиске разложения на простые множители числа `N` можно рассмотреть только числа не превышающие `sqrt(N)`. Наибольшим делителем числа `N` будет `N/a`, где `a` – наименьший простой делитель, а вторым наибольшим делителем будет либо `N/b`, где `b` – второй по величине делитель `N` (также либо простое число, либо `a^2`), либо `a`, если `N` является произведением двух различных простых чисел, либо 1, если `N=a^2`.
var s,t,k,n,i,d:integer;
begin
  read(s);
  for n:=4 to sqr(s-1) do
  begin
    k:=0;
    t:=0;
    d:=1;
    for i:=2 to trunc(sqrt(n)) do
      if n mod i=0 then
      begin
        inc(k);
        t:=t + n div i;
        if i*i<n then d:=i;
        if k=2 then break;
      end;
    if k=1 then
    begin
      t:=t+d;
      inc(k);
    end;
    if (k=2) and (t=s) then
      writeln(n);
  end;
end.
Время работы алгоритма уменьшилось до `O(S^3)`, но остается достаточно большим. Существенно улучшить алгоритм, можно используя самый эффективный способ нахождения простых чисел – решето Эратосфена (вычеркивание из таблицы всех чисел, кратных простым):
const maxn=1000000;
var p:array [2..maxn] of boolean;
  i,j:integer;
begin
  for i:=2 to maxn do
    p[i]:=true;
  for i:=2 to maxn do
    if p[i] then
    begin
      j:=i*i;
      while j<=maxn do
      begin
        p[j]:=false;
        j:=j+i;
      end;
    end;
end.
Для каждого числа нужно найти два наименьших делителя, поэтому массив флагов в алгоритме решета заменяем на два массива целых чисел – наименьший простой делитель и следующий по величине делитель.
const maxn=1000000;
var 
  s,i,i2,j:integer;
  p1,p2:array[2..maxn] of integer;
begin
  read(s);
  for i:=2 to maxn do
  begin
    p1[i]:=1;
    p2[i]:=i;
  end;
  for i:=2 to maxn do
    if p1[i]=1 then { i – простое число }
    begin
      j:=2*i;
      while j<=maxn do
      begin
        if p1[j]=1 then p1[j]:=i
        else if p2[j]>i then p2[j]:=i;
        j:=j+i;
      end;
      { обработка случая, когда вторым делителем
        является квадрат наименьшего делителя }
      if i<=1000 then { чтобы избежать переполнения при умножении }
      begin
        i2:=i*i; 
        j:=i2;
        while j<=maxn do
        begin
          if p2[j]>i2 then p2[j]:=i2;
          j:=j+i2;
        end;
      end;
    end
    else { i – составное число }
      if (i div p1[i])+(i div p2[i])=s then
        writeln(i);
end.
loading