На форумі обговорюються лише питання, пов'язані з олімпіадою
Ви не зайшли.
Задача Simplenumbers:
Тут проходит самое простое решение: просто перебираем все числа от А до Б, проверяем на простоту, и если простое, добавляем +1 для каждой из цифр. Сложность, если считать
, O((b-a)*sqrt(b)).
Можно найти все простые числа решетом Эратосфена, а потом сделать такой же проход (от А до Б). Это будет O((b-a)*log b). Решето Эратосфена работает где-то между O(n) и O(n*log n) (каждое число считается столько раз, сколько у него делителей -отсечения).
Первый код:
{$APPTYPE CONSOLE}
{$B-,R-,O+}
const
maxn=10000;
var
a,b,x:smallint;
i,j:smallint;
d:array[0..9] of longint;
function prost(a:smallint):boolean;
var
i:smallint;
begin
if (a mod 2=0) then
begin
if a=2 then prost:=true else
prost:=false;
end else
begin
i:=3;
while(i*i<=a)and(a mod i<>0)do inc(i,2);
if a mod i=0 then
prost:=false else
prost:=true;
end;
end;
begin
readln(a,b);
fillchar(d,sizeof(d),0);
for i:=a to b do
if prost(i) then
begin
x:=i;
while(x>0)do
begin
inc(d[x mod 10]);
x:=x div 10;
end;
end;
x:=0;
for i:=1 to 9 do
if d[i]>d[x] then
x:=i;
writeln(x);
end.Второй код:
{$APPTYPE CONSOLE}
{$B-,R-,O+}
const
maxn=10000;
var
a,b,x:smallint;
i,j:smallint;
p:array[2..maxn] of boolean;
d:array[0..9] of longint;
begin
readln(a,b);
fillchar(p,b,1);
for i:=2 to round(sqrt(b)) do
if p[i] then
begin
j:=i*i;
while(j<=b)do
begin
p[j]:=false;
inc(j,i);
end;
end;
fillchar(d,sizeof(d),0);
for i:=a to b do
if p[i] then
begin
x:=i;
while(x>0)do
begin
inc(d[x mod 10]);
x:=x div 10;
end;
end;
x:=0;
for i:=1 to 9 do
if d[i]>d[x] then
x:=i;
writeln(x);
end.Задача Measure:
Сортируем по левому краю. Далее делаем проход, поддерживая два числа l и r - начало и конец диапазона, покрываемого на даном шаге. Для i-го отрезка, если a[i].l<=r и a[i].r>r, тогда переносим правый конец r:=a[i].r, если a[i].l>r, то в текущий диапазон больше не попадет ни один отрезок (отсортированы по левому краю), тогда добавляем к общей длине r-l, и "начинаем" новый диапазон l:=a[i].l r:=a[i].r. Сложность O(n*log n)
{$APPTYPE CONSOLE}
{$B-,R-,O+,N+}
const
maxn=10000;
type
interval=record
l,r:extended;
end;
var
n,i:smallint;
a:array[1..maxn] of interval;
l,r:extended;
ans:extended;
procedure Sort(l,r:smallint);
var
i,j:smallint;
k:extended;
x:interval;
begin
if l<r then
begin
i:=l;
j:=r;
k:=a[l+random(r-l+1)].l;
repeat
while a[i].l<k do inc(i);
while a[j].l>k do dec(j);
if i<=j then
begin
x:=a[i];
a[i]:=a[j];
a[j]:=x;
inc(i);
dec(j);
end;
until i>j;
Sort(l,j);
Sort(i,r);
end;
end;
begin
readln(n);
for i:=1 to n do
readln(a[i].l,a[i].r);
Sort(1,n);
ans:=0;
l:=a[1].l-1;
r:=a[1].l-1;
for i:=1 to n do
if r<a[i].l then
begin
ans:=ans+r-l;
l:=a[i].l;
r:=a[i].r;
end else
if a[i].r>r then
r:=a[i].r;
ans:=ans+r-l;
writeln(ans:1:1);
end.Задача Symmetry:
Перебираем каждый символ и пропуск между символами, и от него расходимся в стороны, пока символы на концах совпадают. Так мы переберем все наибольшие палиндромы для каждого возможного "центра", и получим ответ. Этот подход применяется во многих задачах на такие палиндромы. Сложность O(n^2)
{$APPTYPE CONSOLE}
{$B-,R-,O+}
const
maxn=1000;
var
n,i,j:smallint;
a:array[1..maxn] of smallint;
ans,index:smallint;
begin
readln(n);
for i:=1 to n do
read(a[i]);
ans:=1;
index:=1;
for i:=1 to n-1 do
begin
j:=0;
while(i-j>1)and(i+j<n)and(a[i-j-1]=a[i+j+1])do inc(j);
if 2*j+1>ans then
begin
ans:=2*j+1;
index:=i-j;
end;
j:=0;
while(i-j>=1)and(i+j+1<=n)and(a[i-j]=a[i+j+1])do inc(j);
if 2*j>ans then
begin
ans:=2*j;
index:=i+1-j;
end;
end;
writeln(index,' ',ans);
end.Задача Perfectlines:
Можно сделать за O(n^2) с помощью хеширования: Перебираем длину подслова (бинарный поиск не пройдет). Составляем хеш-коды всех подстрок заданой длины (k), это можно сделать за линейный проход, сдвигаясь на одну букву. Потом для каждого i=1,n-2k+1 рассматриваем i-й символ, как начало вхождения T. Если T+T входит начиная с i-го символа, то хеш-функции f[i] и f[i+k] должны быть равны изза необходимого равенства подстрок. Если они равны, делаем посимвольное сравнение, и в случае удачи записываем результат. В среднем это будет O(n^2). Для тех, кто не знает: хеш-функция чего-то - это некоторый его "отпечаток", например для строки в качестве хеш-функции можно взять ее представление как числа в какой-то системе счисления (например, с основанием b=257), взятое по какому-то модулю M (например, 299993). Так, строка a1,a2,...,an будет именть хеш-функцию (a1*b^(n-1)+a2*b^(n-2)+...+a(n-1)*b+an)mod M. Значение этой хеш-функции будет целым числом от 0 до M-1. Если строки равны, то и их хеш-функции должны быть равны. Обратное, понятно, не всегда верно, но если строк будет даже до 10000, а модуль порядка 300000, то вероятность этого очень низкая. В одну ячейку попадет максимум несколько строк (O(1)
). Модуль и базу желательно брать простым числом, или хотя бы не делящиеся на 2,3,5.
{$APPTYPE CONSOLE}
{$B-,R-,O+}
const
maxn=100;
base=257;
modul=299993;
var
s:string;
a:array[1..maxn] of smallint;
n,i,j,k:smallint;
f:array[1..maxn] of longint;
tek,maxdigpow:longint;
flag:boolean;
begin
readln(s);
n:=length(s);
for i:=1 to n do
a[i]:=Ord(s[i])-Ord('a');
flag:=false;
k:=n div 2+1;
while(k>0)and(not flag)do
begin
dec(k);
tek:=0;
maxdigpow:=1;
for i:=1 to k do
begin
tek:=(tek*base+a[i])mod modul;
maxdigpow:=(maxdigpow*base)mod modul;
end;
f[1]:=tek;
for i:=2 to n-k+1 do
begin
tek:=(tek*base+modul-(maxdigpow*a[i-1])mod modul+a[i+k-1])mod modul;
f[i]:=tek;
end;
for i:=k+1 to n-k+1 do
if f[i]=f[i-k] then
begin
j:=0;
while(j<k)and(a[i+j]=a[i-k+j])do inc(j);
if j=k then
flag:=true;
end;
end;
writeln(k);
end.Поза форумом
Молодец ![]()
Теперь свои 5 копеек...
В первой задаче у меня, опять же, предпросчет всех чисел, только я решил сэкономить еще больше времени и засунул их в ansistring, i-тый символ которого равен '1', если число простое, или '0', если нет; в результате размер исходника возрос до 10+ Кб, но это все еще в пределах допустимости ![]()
Цифры находил обычным div-mod'овским методом. Если не учитывать эту особенность — вполне O(N).
Во второй задаче у меня тоже O(N). Считаем, что каждое начало отрезка — это открывающая скобочка, каждый конец — закрывающая скобочка. Проходим массив слева направо, если попалась открывающая скобочка (или несколько открывающих скобочек в одном месте), то увеличиваем некий счетчик на количество этих самых скобочек. Для закрывающих скобочек, соответственно, счетчик уменьшаем. Если на очередном шаге счетчик больше нуля — увеличиваем ответ на единицу. И ничего не надо сортировать ![]()
В третьей задаче я передвигал предполагаемый центр палиндрома слева направо, и для каждого значения центра проверял, совпадают ли элементы, отстоящие на N позиций слева и N позиций справа от центра; если они совпадают, N увеличивается, и так происходит, пока найдется несовпадение, либо когда дальше двигаться уже будет нельзя (тогда вся строка — палиндром). Учтены два случая: когда центр состоит из одного элемента (длина палиндрома нечетная) и двух (четная соответственно). В результате — N^2 + N^2 = O(2*N^2).
Перебор можно было усовершенствовать, но мне было лень, и в итоге получилось 27 баллов вместо 30, хотя онлайн-проверка дает все 30 ![]()
В четвертой задаче я сделал просто полный перебор (N^3), прошло все тесты.
Исходники здесь: http://drop.io/s_olymp
Поза форумом
guest1 написав:
Цифры находил обычным div-mod'овским методом. Если не учитывать эту особенность — вполне O(N).
Это как раз дает O(N*log N) (2^(k-1)<N<=2^k. k=log N)
guest1 написав:
Во второй задаче у меня тоже O(N). Считаем, что каждое начало отрезка — это открывающая скобочка, каждый конец — закрывающая скобочка. Проходим массив слева направо, если попалась открывающая скобочка (или несколько открывающих скобочек в одном месте), то увеличиваем некий счетчик на количество этих самых скобочек. Для закрывающих скобочек, соответственно, счетчик уменьшаем. Если на очередном шаге счетчик больше нуля — увеличиваем ответ на единицу. И ничего не надо сортировать
Да, точно:) Числа множим на 10 и получаем целые.
Поза форумом
partisan написав:
Это как раз дает O(N*log N) (2^(k-1)<N<=2^k. k=log N)
Минут 10 думал — наконец, понял, что этих самых div-mod'овских операций будет столько, какова длина нашего числа. Я-то думал, при чем здесь эти логарифмы
Голова к вечеру уже не работает...
Поза форумом
Еще один вариант решения 1-ой задачи (написан на С++, проходит все тесты). Алгоритм похож на 1-ое решения, предложенное partisan, но есть некоторые отличия в реализации (не связанные с языком программирования), кроме того отбрасываются числа, кратные 3.
#include <stdio.h>
#include <iostream.h>
#include <math.h>
#define BASE 10
#define MIN_DIV 5
#define DELTA 2
int main()
{
int max, max_div, min, div, i, cur_i, mod3_div, mod3;
int dig_ar[BASE] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
cin >> min >> max;
if (min == 2)
dig_ar[2]++;
i = min | 1;
if (i == 3)
{
dig_ar[3]++;
i = 5;
}
if ((mod3 = - (i % 3)) == 0)
{
i += DELTA;
mod3 = -DELTA;
}
while (i<= max)
{
for (div = MIN_DIV, max_div = int(sqrt(i)), mod3_div = -DELTA; div <= max_div; div += DELTA)
{
if (i % div == 0)
goto cont;
if (++mod3_div == 0)
{
mod3_div = -DELTA;
div += DELTA;
}
}
cur_i = i;
while (cur_i >= BASE)
{
dig_ar[cur_i % BASE]++;
cur_i /= BASE;
}
dig_ar[cur_i]++;
cont:
i += DELTA;
if (++mod3 == 0)
{
mod3 = -DELTA;
i += DELTA;
}
}
max = dig_ar[(cur_i = BASE - 1)];
for (i = BASE - 2; i >= 0; i--)
{
if (dig_ar[i] >= max)
max = dig_ar[(cur_i = i)];
}
cout << cur_i;
return 0;
}
Поза форумом