Задача A: Ответ равен floor(N*M*0.5). Поскольку на доске N*M клеток, и каждая доминошка покрывает ровно 2 клетки, то больше положить точно нельзя. Теперь покажем, как положить ровно столько доминошек. Если N чётно, то кладём M рядов по N/2 доминошек и занимаем всю доску. Если N нечётно, то укладываем полностью (N-1) ряд доски как написано выше, а последний ряд забиваем floor(M/2) доминошками. При этом максимум останется одна незанятая клетка, в случае если N и M нечётны.
Задача B: Разумеется нужно было написать неквадратичное решение. Проходим по заданной строке и считаем, сколько раз встречается каждый символ. Пусть Ki - количество вхождений символа с ASCII-кодом i в строку S. Тогда ответ равен sum(Ki2). В коде решения часто возникали проблемы:
- Писали квадратичное решение (двойной цикл по строке).
- Забывали поставить int64. Либо в ответе, либо в возведении Ki в квадрат.
- Неверное выводили int64.
Все эти проблемы хакались тестом из 100000 одинаковых символов. Из-за этого процесс хакания превратился в игру "кто быстрей захакает очередное решение B", поскольку новички часто допускали все эти ошибки. Эта проблема ещё усилилась из-за того, что GCC-компилятор на сервере оказывается является MinGW-компилятором, поэтому на нём не работает вывод printf("%lld", ...). Ещё одна странная проблема, возникшая у хакеров - молчаливое обрезание теста по длине около 30000 при копировании его в редактор. Естественно, переполнение в решении из-за этого исчезало.
Задача C: В задаче требовалось, чтобы все коровы находились строго внутри маршрута. Изначально я планировал сделать нестрого, но тогда ответ для случая одной коровы оказался бы странным=) На самом деле разница в решении невелика. Ответ для "строгой" задачи всегда на 4 больше, чем для "нестрогой". Некоторые участники предпочли добавить к каждой точке четырёх её соседей крестиком, чтобы перейти к нестрогой задаче.
Нестрогая задача решается следующим образом. Заметим, что искомый маршрут всегда можно представить восьмиугольником, возможно с нулевыми сторонами. Нужно было взять четыре пары параллельных прямых и "прижать" их к точкам. Иначе говоря, мы находим для набора точек ограничивающий прямоугольник, параллельный осям координат. Затем находим ограничивающий прямоугольник для набора точек на повёрнутой на 45 градусов картинке. Теперь пересекаем эти два прямоуольника (как заполненные фигуры). Получаем в общем случае восьмиугольник, который является искомым. Для реализации нужно посчитать минимумы и максимумы X, Y, X+Y, X-Y по всем точкам, потом посчитать по ним ответ по нетривиальным формулам.
Многие участники решили задачу по-другому. Легко понять, что для перемещения на вектор (X, Y) пастуху требуется max(|X|, |Y|) ходов. Так вот, можно заставить пастуха ходить по вершинам выпуклой оболочки. Нужно запустить стандартный алгоритм Грэхема, потом обойти вершины на оболочке и просуммировать max(|X'-X|, |Y'-Y|) для соседних вершин в ней. Это решение также правильное.
Была идея сделать ограничение координат по модулю до 109, но потом решили, что незачем лишний раз требовать int64. Возможно лучше бы int64 было в этой задаче, а не в задаче B.
Задача D: Условие задачи выглядит запутанно и воинственно. Дело в том, что легенда была навеяна занятиями по гражданской обороне в университете. Это такой предмет, на котором в частности учат, что делать до и после ядерного взрыва...
Сперва нужно заметить, что чем больше боеголовка, тем больше вероятность выполнить задание. Это вполне разумно с житейской точки зрения. Функция P(D, R) возрастает по R при фискированном D. Очевидно, что при этом и вероятность повредить как минимум K объектов тоже возрастает по R. Следовательно, можно было найти искомый радиус бинарным поиском. Для того, чтобы это сделать, нам нужно научиться находить вероятность выполнить задачу при фиксированном и известном радиусе R.
Теперь нужно решить такую задачу: есть N объектов, каждый из которых разрушается с заданной вероятностью (pi). Нужно найти вероятность разрушения как минимум K из них. Задача решается динамическим программированием, очень похожим на подсчёт биномиальных коэффициентов по треугольнику Паскаля. Пусть R[i, j] - вероятность поразить ровно j объектов среди первых i из всех заданных. Если мы посчитаем все значения R для 0<=j<=i<=N, то ответ можно будет вычислить как sum_{j=k..N} R[N, j]. Начальное условие динамики: R[0, 0] = 1. Пересчёт выполняем по формуле R[i, j] = R[i-1, j-1] * pi + R[i-1, j] * (1-pi), считая, что все элементы R с j<0 или j>i нулевые.
Экспериментально было выяснено, что многим кодерам (мне в том числе) в голову первым делом приходит идея учитывать только K ближайших к эпицентру строений. То есть считать, что вероятность выполнить задачу равна просто p1 * p2 *... * pK. Это неверно: например, если есть два строения на одинаковом расстоянии, то вероятность поразить хотя бы одно из них равна 1-(1-p)2 вместо p. Но я решил сделать тем, кто так посчитал, приятное и включил в претесты только такие случаи, когда это решение работает=) Так что это неверное решение специально проходило все претесты.
Задача E: Пусть D = b2-c - четверть дискриминанта уравнения. Решения уравнения равны -b-sqrt(D) и -b+sqrt(D). Таким образом, есть два типа корней: иррациональные корни вида x +- sqrt(y) и целые корни. Будем считать количество корней отдельно для двух типов.
Иррациональные корни имеют вид x+sqrt(y) (y - не точный квадрат). Утверждается, что если два числа такого вида равны, то их x и y совпадают. Это можно легко проверить на бумажке, главное принять во внимание, что sqrt(y) иррационально. Таким образом, если два иррациональных корня равны, то и уравнения совпадают. Следовательно все иррациональные корни всех уравнений различны. Будем перебирать b=1..N. Для каждого из них нужно найти количество уравнений с положительным дискриминантом, не являющимся точным квадратом. То есть количество c=1...M таких, что D = b2-c - не точный квадрат. Можно определить отрезок [L; R], значения которого принимает D (L >= 0). Потом взять его длину (R-L+1) и вычесть количество точных квадратов в нём. Это и будет количество уравнений с иррациональными корнями для фиксированного b. Умножаем это число на два (корня два) и прибавляем к ответу.
Теперь разберёмся с целыми корнями. Для фиксированного b мы определили отрезок [L;R] для D. Тогда sqrt(D) будет принимать целые значения [l; r] = [ceil(sqrt(L)); floor(sqrt(R))]. Подставляем это множество значений в формулу для корня и получаем, что целые корни всех уравнений с фиксированным b составляют отрезки [-b-r;-b-l] и [-b+l;-b+r]. Однако целые корни существенно могут быть одинаковыми. Поэтому мы должны пометить в глобальной структуре данных, что все корни из этих двух отрезков "найдены". После того, как мы переберём все b=1..N, в этой структуре данных нужно найти количество чисел, помеченных хотя бы раз.
Такая структура данных реализуется на массиве. Все целые корни уравнений лежат в пределах от -10000000 до 0. Заводим массив Q чуть большего размера. Для того, чтобы отметить отрезок [s; t], мы увеличиваем на 1 элемент Q[s] и уменьшаем на 1 элемент Q[t+1]. В конце работы алгоритма пробегаем весь массив Q слева направо и считаем префиксные суммы в массив S. То есть S[i] = Q[-10000010] + Q[-10000009] + ... + Q[i-1] + Q[i]. Тогда S[i] будет означать то, сколько раз мы всего пометили число i. Теперь пробегаем массив S и считаем количество ненулевых элементов - это и есть количество различных целых корней.
Решение работает за O(N), поскольку требуется перебирать все значения b. Очень вероятно, что есть чисто комбинаторная (O(1)) формула для ответа=)
Итого. Я считаю, что в зачадах раунда были две проблемы. Первая - отсутствие технически сложной задачи. Из-за этого кодеры высокого уровня решили все задачи за первый час, и мучались с хаканием в нестабильной системе остальной час. Мне кажется, задача E в раунде должна быть действительно сложной для написания. Вторая проблема - огромное количество хаков по задаче B. Надо было всё-таки сделать длину строки до 40000. Ещё интересный вопрос: почему смешиваются дивизионы? Было бы разумнее, если каждая комната была либо полностью первым дивизионом, либо полностью вторым. Тогда кодеры первого дивизиона будут хакать решения сложных задач, а не ловить элементарные ошибки новичков.
Напоминаю, что в системе тестирования вы можете посмотреть тесты к задачам.
Ну если все корни имеют вид x+sqrt(y) - да. Только, там так же есть корней вида x-sqrt (y). Почему нету корней первово вида равны кокому-то корню второго вида (в током случае уравнения , для которых это получается, точно будут разные)?
Их нет. Это проверяется на бумажке совершенно аналогичным образом...
Я надеюсь, это более-менее понятно=)
#include<iostream>
#include <string>
using namespace std;
int mx,i,a[1000000],mn;
long long sum,t;
int main()
{
string str;
mx=0;
cin>>str;
for(i=0;i<str.size();i++)
{
t=str[i];
if(t>mx)
mx=t;
a[t]++;
}
for(i=0;i<=mx;i++)
sum+=(a[i]*a[i]);
cout<<sum<<endl;
return 0;
}
Here is the problem:
sum+=(a[i]*a[i]);
Here you multiply two numbers of type int and receive result in type int. But the result can be 1010.
Clearly both roots are < 0 ( product is positive & sum is negative).
Let roots be -p & -q. ( p & q are positive integers ).
we have p + q <= 2 * N . Also p * q <=M.
Take an odd integer o. Lets see when would we have an equation having -o as a root. Other root should also be odd & hence absolute value at least 1 . So for all odd o st 1<=o <= min( 2 * N -1 , M) there is an equation which has -o as root : (x + o) ( x + 1)
Similarly for all even integers e, st 1 <= e <= min ( 2N -2, M/ 2) there is an equation with -e as root : ( x + e) ( x + 2 )
So negatives of integer roots are just odd positive integers up till min (2N-1, M) with even positive integers up till min ( 2 N -2 , M/2)
Desired number is = (min( 2N-1, M) + 1 ) / 2 + min ( 2 N -2 , M/ 2) / 2
In problem E to find integer roots I just iterated over all possible roots x and for each of them over all possible coefficients c (they must be divisible by x). Knowing x and c, we can find the second root and coefficient b and check the conditions. The time is .
Предмет "Гражданская оборона" повествует о том, как предупреждаются и предотвращаются различные чрезвычайные ситуации. Вопрос: зачем это знать обычным студентам ВУЗа? Обоснование примерно следующее: ЧС иногда происходят. Обычно эти проблемы решают соответствующие ведомства (МЧС, например). Но может случиться, что в критической рядом подобного специалиста не окажется. А решения принимать надо. Поскольку люди с высшим образованием должны быть компетентными руководителями, им желательно знать, что делать. Или ещё один аргумент за этот предмет: в будущем выпускники ВУЗов часто становятся руководителями каких-то предприятий. Например, можно стать директором какого-нибудь отделения банка. Такое лицо несёт ответственность за профилактические противопожарные меры в здании. Или другой пример: если кто-то стал владельцем ночного клуба, то он должен понимать, что в закрытом помещении нельзя запускать фейерверки=)
У нас в НГУ к тому же была военная кафедра, пока её не распустили. Она располагалась на том же этаже, где сейчас идут занятия по ГО, так что нетрудно предположить, что распущенная кафедра частично преобразовалась в ГО. Так что в НГУ предмет ГО есть у всех=) Мне повезло: у меня был не слишком старый преподаватель совершенно не военного профиля (он один из тех, кто следит за соблюдением порядков ГО в НГУ). Некоторым везёт меньше...
Вообще ГО зародилась ещё в Советском союзе. Большая часть её норм никак не изменилась с тех времён. С одной стороны - это хорошо, потому что они очень эффективны. С другой стороны, там есть некоторые атавизмы. Например, меры профилактики и предотвращения последствий атаки ядерным, биологическим и химическим оружием сегодня кажутся дикостью=) А вот во времена холодной войны это было ожидаемым событием. На меня плохо подействовали рассказы о видах радиации, их распространения, дозах, ЭМИ, лучевой болезни и т.д. и т.п. Да и язык, который принят в военных и ГО кругах, сух, скуп и формален. Вот я это в легенду задачи и записал...
P.S. Мой преподаватель подтвердил, что в холодильнике ядерный взрыв не пережить.
My code is below (www.codetolearn.blogspot.com):
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader bf=new BufferedReader(new InputStreamReader(System.in));
String ans=bf.readLine();
String var[]=ans.split(" ");
HashMap<Integer, Double> val=new HashMap<Integer, Double>();
double dx=0;
int cnt=0;
double rt1=0;
double rt2=0;
val.put(null, (double)1);
val.put(null, (double) 2);
for(double i=1;i<=Integer.parseInt(var[0]);i++){
for(double j=1;j<=Integer.parseInt(var[1]);j++){
dx= Math.pow(2*i, 2)-(4*j);
if(dx>0){
rt1= (((-2*i)+ Math.sqrt(dx)))/2;
rt2= (((-2*i) - Math.sqrt(dx)))/2;
if(val.get(cnt)==null){
cnt=cnt+1;
val.put(cnt,(double)rt2);
}
if(val.get(cnt)==null){
cnt=cnt+1;
val.put(cnt,(double)rt1);
}
if(val.containsValue(rt1) &&val.containsValue(rt2)){
}else if(val.containsValue(rt1)){
cnt=cnt+1;
val.put(cnt,(double)rt2);
}else if(val.containsValue(rt2)){
cnt=cnt+1;
val.put(cnt,(double)rt1);
}else{
cnt=cnt+2;
val.put(cnt,(double)rt1);
val.put(cnt,(double)rt2);
}
}else if(dx==0){
rt1=(double) ((-2*i)/2);
if(val.get(1)==null){
cnt=cnt+1;
val.put(cnt,(double) rt1);
}
if(val.containsValue(rt1)){
}else{
cnt=cnt+1;
val.put(cnt,(double)rt1);
}
}
}
}
System.out.println(cnt);
}
}
Просмотрев решение некоторых участников, я не могу понять, что в задаче С делается в этих строках:
if x[i]+y[i]<d1 then d1:=x[i]+y[i];
if x[i]+y[i]>d2 then d2:=x[i]+y[i];
if x[i]-y[i]<d3 then d3:=x[i]-y[i];
if x[i]-y[i]>d4 then d4:=x[i]-y[i];
Подскажите, пожалуйста, кто-нибудь в чём суть этих строк, или подскажите где можно об этом почитать. Спасибо.
(offtopic) Чтобы получить внутри формулы настоящий минус ширины ~плюса, а не дефис, почему-то надо писать знак минуса два раза.
Подскажите в чём проблема с задачей B...
Собственно суть дела:
21 тест, пишет не правильный ответ...Понял свою ошибку, исправил прогу...
Специально делаю вывод из файла, формирую файл с 10^5 'с' и проверяю свою прогу! Ответ ПРАВИЛЬНЫЙ!!! Отправляю...Пишет что не пройден этот же 21-ый тест и выводится тот же ответ(а не мой правильный) который был в начале...
Что делать???
P.S. Переменные обнулял...
Кстати вот сам код:
Program problem2;
var a:array[0..256]of int64;
s:ansistring;i,n:longint;sum:int64;
Begin
read(s);
n:=length(s);
for i:=1 to n do
inc(a[ord(s[i])]);
sum:=0;
for i:=0 to 256 do
if a[i]<>0 then sum:=sum+sqr(a[i]);
writeln(sum);
End.
Да ,ответ выводит 1410065408, что соответственно равно 10^10 mod 2^32...
Отправил... Полное РЕШЕНИЕ!!!
Спасибо большое!!!
P.S. Проблема была с квадратом числа(значит компилятор на КФ не распознаёт sqr(a) когда a:int64)..Буду знать!
Компилятор: Free Pascal 2.4.0
Может на КФ стоит обновить компилятор Free Pascal