Блог пользователя Egor

Автор Egor, 18 месяцев назад, По-русски

Задача A: Кольцевая

Это довольно простая задача - мы имеем цикл, можем ориентировать его в одну из 2х сторон. Осталось посчитать стоимость ориентации цикла в обе стороны и выбрать меньшую.
Есть небольшой трюк - можно посчитать стоимость только для одной ориентации. Стоимость другой - это суммарная стоимость всех ребер минус стоимость первой

Задача B: Чемпионы Формулы-1

Тоже очень простая задача - надо сделать буквально то, что просят. А именно - завести ассоциативный массив, в котором каждому пилоту соответствуют количество очков и массив из 50 элементов - количество раз, которое он занял соответствующее место. Далее осталось найти максимум по 2м критериям.

Задача C: Последовательность точек

Заметим, что последовательное отражение от 2 точек - это параллельный сдвиг на удвоенный вектор между этими 2мя точками. Таким образом M2n = M0, так как последовательность отражений превратится в последовательность сдвигов на удвоенные вектора A0A2, A2A4, ..., An - 2A0 - а они в сумме дают нулевой вектор. Таким образом можно j заменить на j' = jmod2N. Теперь можно просто симулировать j' отражений. Пусть нам надо найти M' (x', y') - отражение точки M(x, y) относительно точки A(x0, y0). Тогда x' = 2x0 - x, y' = 2y0 - y.

Задача D: Сломанный робот

Если робот находится в последнем ряду, то ответ, очевидно, 0. Пусть теперь мы для каждой клетки следующего ряда знаем матожидание числа шагов из этой клетки до последнего ряда - zi. Пусть xi - это матожидание числа шагов для i-й клетки в текущем ряду. Получаем систему уравнений:
x1 = 1 + x1 / 3 + x2 / 3 + z1 / 3
xi = 1 + xi / 4 + xi - 1 / 4 + xi + 1 / 4 + zi / 4 для i от 2 до M - 1
xM = 1 + xM / 3 + xM - 1 / 3 + zM / 3
Это трехдиагональная система, она решается методом прогонки за линейное время.
Осталось применить это решение N - i раз и взять j-й элемент ответа.
Отдельным представляется случай M = 1 - в этом случае уравнение немного меняется, но на самом деле легко заметить, что ответ 2(N - i), так как матожидание спуска на один ряд - 2.

Задача E: Берляндский коллайдер

Исключим для начала ответ -1 - ответ -1 получается только если у нас сначала идет какое-то число частиц (возможно, нулевое), движущихся влево, а затем какое-то число частиц (возможно, нулевое), движущихся вправо.
Теперь будем искать ответ бинарным поиском. Максимальный возможной ответ - 1e9 (частица с координатой -1е9 и скоростью 1 и частица с координатой 1е9 и скоростью -1). Рассмотрим чсило t. Как нам определить, ответ больше t или меньше? Пройдемся по частицам слева направо, при этом будем запоминать самую правую точку, до которой долетела частица, которая летит направо. Тогда если мы встретим частицу, которая летит налево и которая залетела левее этой точки, ты мы нашли пару частиц, которые столкнулись в момент времени меньше t. Если мы ни одной такой частицы не нашли - значит, первое столкновение произошло в момент времени больше t. Теперь надо аккуратно следить за правильным условием окончания бинарного поиска (например, while (r - l > 1e-11 * r)) и вывести потом r с 11, например, знаками после запятой
 
 
 
 

 
18 месяцев назад, # | Ответить
  Проголосовать: нравится 0 Проголосовать: не нравится
А что таки случилось с TeX тегом frac?
 
18 месяцев назад, # | Ответить
  Проголосовать: нравится +5 Проголосовать: не нравится
Надо бы еще заголовок в английской версии перевести
 
18 месяцев назад, # | Ответить
  Проголосовать: нравится 0 Проголосовать: не нравится
Another solution for problem D:

Let's define function F(a,b) which returns X where X = 1 + X * a + (1-a) * b and e[i][j] is the expected number of steps that takes for robot to reach last row when robot is initially on cell(i,j).
Here is the whole code:

for (int i=1;i<=m;i++)
    e[n][i]=0;
 
for (int i=n-1;i>=1;i--){
    if (m == 1){
       e[i][1]=f(1.0/2,e[i+1][1]);
    }else{
      for (int count=1;count<=30;count++){
        e[i][1]=f(1.0/3,(e[i+1][1]+e[i][2])/2);
        e[i][m]=f(1.0/3,(e[i+1][m]+e[i][m-1])/2);
        for (int j=2;j<m;j++)
           e[i][j]=f(1.0/4,(e[i][j+1]+e[i][j-1]+e[i+1][j])/3);
      }
    }
  }
  •  
    18 месяцев назад, # ^ | Ответить
      Проголосовать: нравится 0 Проголосовать: не нравится
    Oh..There is no need to use F(a,b)
    Let the origin Array to be A[1..m]..represents the previous Floor..then first put all elements in B to be zero.
    then create Array C where C[i]=(B[i]+B[i-1]+B[i+1]+A[i])/4+1..then Let B=C and do it for many times can also calculate a new A...
  •  
    18 месяцев назад, # ^ | Ответить
      Проголосовать: нравится 0 Проголосовать: не нравится
    are 30 iterations enough for achieving 1e-5 accuracy? Is there any reason why 30 are enough ? Ive tried 100 iterations before for around 500 variables, and I couldnt achieve 1e-9 accuracy.
 
18 месяцев назад, # | Ответить
  Проголосовать: нравится -3 Проголосовать: не нравится
По поводу разбора задачи Е:
"Пройдемся по частицам слева направо, при этом будем запоминать самую правую точку, до которой долетела частица, которая летит направо. Тогда если мы встретим частицу, которая летит налево и которая залетела левее этой точки, ты мы нашли пару частиц, которые столкнулись в момент времени меньше t"
На мой взгляд более корректно запоминать самую правую для летящих направо и самую левую для летящих налево, а потом уже делать вывод об увеличении или уменьшении t.
  •  
    18 месяцев назад, # ^ | Ответить
      Проголосовать: нравится 0 Проголосовать: не нравится
    Что значит "более корректно"?
    Вот это решение проходит
    •  
      18 месяцев назад, # ^ | Ответить
        Проголосовать: нравится +2 Проголосовать: не нравится
      Действительно, мое суждение было поспешным. Приношу извинения автору разбора.
 
18 месяцев назад, # | Ответить
  Проголосовать: нравится 0 Проголосовать: не нравится
In problem E , althought I use binary search , it's also Time limited,
would you please tell why?    -_-!!!
my binary search code:

int judge(double t){
 double now = -1500000000;
 for(int i=1;i<=n;i++){
  if(node[i].type==1){
   double test = node[i].x + node[i].v * t;
   if(test > now) now = test;
  }
  else{
   double test = node[i].x - node[i].v*t;
   if(test<=now+epx) return 1;
  }
 }
 return 0;
}

// in main function
double left = 1e-10, right = (node[n].x - node[1].x)/2.0  + 1;
while(aabs(left-right)>epx){
     double mid = (left + right)/2;
     if(judge(mid)==1) right = mid;
     else  left =  mid;
 }

  •  
    18 месяцев назад, # ^ | Ответить
      Проголосовать: нравится 0 Проголосовать: не нравится
    That's because left and right will never be within epx. But you only need to find ans within relative or absolute difference of 1e-9/ So you need to change condition to aabs(left-right)>epx * max(right, 1)
    •  
      18 месяцев назад, # ^ | Ответить
        Проголосовать: нравится 0 Проголосовать: не нравится
      Насколько корректна будет замена условия типа while() на выполнение log2(1e18) итераций бинарного поиска?
      •  
        18 месяцев назад, # ^ | Ответить
          Проголосовать: нравится 0 Проголосовать: не нравится
        я думаю, что пройдет
      •  
        18 месяцев назад, # ^ | Ответить
          Проголосовать: нравится +3 Проголосовать: не нравится
        Видел реализации, в которых сделали ~200 итераций и они прошли
  •  
    18 месяцев назад, # ^ | Ответить
      Проголосовать: нравится +1 Проголосовать: не нравится
    Replace any cin's with scanf's, if you have any.
 
18 месяцев назад, # | Ответить
  Проголосовать: нравится +1 Проголосовать: не нравится
На контесте придумалось линейное решение E без бинпоиска, но не прошло по времени из-за глупой и неоптимальной реализации.
Разобъем множество точек на чередующие куски подряд идущих точек из летящих только вправо или только влево. Очевидно, что первое столкновение произойдет между соседними кусками (-> X <-).
Заметим еще тот факт, что в каждый момент до столкновения в каждом "куске" ровно одна частица имеет шанс поучаствовать в первом столкновении - самая дальняя по ходу движения. Проходя по куску против хода движения, при помощи стека за один проход найдем все частицы, которые когда-либо окажутся первыми, вместе с временными отрезками, когда они ими будут.
Теперь посмотрим на два соседних "встречных" куска. Имеет смысл рассматривать только те точки из этих кусков, которые в некоторый момент окажутся первыми. После рассмотрения любой такой пары легко определить хронологически следующюю: надо всего лишь посмотреть, в каком из кусков раньше поменяется первая точка. Каждая пара кусков даст нам не более (количество точек в этих кусках) рассмотренных пар.
Если же встречных пар нет, ответ -1.
  •  
    18 месяцев назад, # ^ | Ответить
      Проголосовать: нравится 0 Проголосовать: не нравится
    >>Проходя по куску против хода движения, при помощи стека за один проход найдем все частицы, которые когда-либо окажутся первыми, вместе с временными отрезками, когда они ими будут.

    Можно поподробнее, как?
    •  
      18 месяцев назад, # ^ | Ответить
        Проголосовать: нравится +3 Проголосовать: не нравится
      Ок.
      Пусть наш стек - это ответ для нескольких первых частиц (против хода движения). Скорости частиц в нем убывают от вершины. Новую частицу стоит рассматривать, только если ее скорость больше всех рассмотренных, поскольку иначе есть частица, которая дальше и (нестрого) быстрее ее. Также, новая быстрая частица всегда попадет в стек, т.к. через достаточно большое время она станет первой.
      Теперь надо понять, какие частицы выкинуть. Посмотрим на частицу в вершине стека. Если наша новая частица обгоняет ее раньше, чем та становится первой (т.е. обгоняет вторую), выбрасываем ее из стека. Это действие надо повторять, пока не окажется, что частица в вершине стека некоторое время все же пребывает первой. Остальные частицы в стеке, очевидно, рассматривать не надо.
      Временные отрезки определить просто - это отрезок от того момента, когда частица обгоняет следующую в стеке частицу (либо 0 для конца), до момента обгона более быстрой предыдущей (либо бесконечность для последней).
 
18 месяцев назад, # | Ответить
  Проголосовать: нравится +1 Проголосовать: не нравится
Я решал задачу C возведением матрицы в степень, интересно были ли ещё такие умники? 
  •  
    18 месяцев назад, # ^ | Ответить
      Проголосовать: нравится 0 Проголосовать: не нравится
    Вряд ли. Жалко, что задача была куда проще, идея очень красивая :)
  •  
    18 месяцев назад, # ^ | Ответить
      Проголосовать: нравится 0 Проголосовать: не нравится
    я возводил матрицу в степень)) ибо не заметил слово "нечетное".
    в принципе, ничего страшного, за 15 минут вбил и сдал, только вот сомневался влезет ли в long long. ибо с четным количеством точек легко получить координаты порядка 1021. а оно взяло да и зашло...))
    •  
      18 месяцев назад, # ^ | Ответить
        Проголосовать: нравится 0 Проголосовать: не нравится
      Если число точек четное, то композиция симметрий относительно них — параллельный перенос, так что в этом случае тоже матрица не нужна.
      •  
        18 месяцев назад, # ^ | Ответить
          Проголосовать: нравится 0 Проголосовать: не нравится
        я закодил первое, что пришло мне в голову))
 
18 месяцев назад, # | Ответить
  Проголосовать: нравится +3 Проголосовать: не нравится
Earlier i didnot know that u are the winner of GOOGLE CODE JAM 2010
Congrats egor !!!
 
18 месяцев назад, # | Ответить
  Проголосовать: нравится 0 Проголосовать: не нравится
congrats egor!!...nice tutorial :)