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

Автор Seyaua, 13 лет назад, По-русски

Здесь приводится разбор всех задач прошедшего раунда. Вопросы и предложения задавайте в комментариях. Отдельное спасибо пользователю Zlobober за разбор задачи С из первого дивизиона, который я использовал в качестве авторского разбора.

Дивизион 2, задача А. Петя и строки

В данной задаче сначала нужно было привести обе строки к одинаковому регистру (например сделать все буквы маленькими), а затем просто сравнить их лексикографически, пройдя слева направо по символам.

 

Дивизион 2, задача B. Петя и квадрат

Оказывается, что если мы хотим разделить квадрат на две равные части, то линия разреза должна проходить через центр квадрата. Отсюда становится понятно, что если отмеченная клетка содержит центр квадрата, то ломаную провести невозможно и нужно вывести NO, иначе вывести YES. Получаем код, которой решает данную задачу:

scanf("%d%d%d", &n, &x, &y);

n /= 2;

if ((x == n || x == n + 1) && (y == n || y == n + 1)) printf("NO\n"); else printf("YES\n");

 

Дивизион 2, задача С. Петя и неравенства (Дивизион 1, задача А)

Нетрудно видеть, что максимум суммы квадратов достигается, когда все числа, кроме одного равны 1. Теперь достаточно проверить, можем ли мы из заданного y набрать n положительных целых чисел и если можем, то проверить, что x ≤ 1 + 1 + ... + (y - (n - 1))2.

 

Дивизион 2, задача D. Петя и делители (Дивизион 1, задача B)

Заведем массив used, такой, что в used[j] хранится номер последнего числа во входных данных, у которого есть делитель j. Тогда для каждого запроса будем последовательно перебирать делители числа xi и для каждого делителя k числа xi будем проверять является ли он «уникальным». После этого обновим пометку для делителя k в массиве used.

 

Дивизион 2, задача E. Петя и пауки (Дивизион 1, задача С)

Данная задача подразумевала множество решений. Следует отметить, что количество входных данных не так велико, поэтому можно было попытаться вручную разобрать все возможные входные данные. Можно было написать перебор, который при нужных отсечениях можно было сдать даже без предпросчета ответов. Однако большинство решений участников все же содержало в себе динамику по профилю. Здесь я приведу решение пользователя Zlobober.

Будем считать не максимальное количество оставшихся клеток, а минимальное количество центров, в которые могут сбежаться все пауки. Повернём доску, чтобы количество строк было не больше шести.

Пусть D[k][pmsk][msk] - это минимальное количество центров, достаточное, чтобы собрать всех пауков с первых k столбцов, причём k-ый столбец имеет маску pmsk, а k + 1-ый - msk. Тогда за переход мы перебираем маску tmsk k - 1-ого столбца, и среди тех, для которых k-ый столбец может целиком разбежаться по центрам, выбираем тот, для которого D[k - 1][tmsk][pmsk] минимально.

Получается решение за O(n*23m), где n > m.

 

Дивизион 1, задача Д. Петя и раскраски

Заметим, что если m = 1, то ответ kn, так как допустимы все возможные раскраски.

Теперь m > 1. Рассмотрим первый столбец доски (проведем вертикальную прямую справа от первого столбца). Пусть в нем x различных цветов. Тогда в оставшихся столбцах тоже x различных цветов по условию. Теперь если мы сдвинем нашу вертикальную прямую на 1 вправо, то количество цветов в левой части не уменьшится, а в правой не увеличится. Повторяя сдвиги, можем сделать вывод, что опять в левой части будет x различных цветов и в правой тоже x. Теперь мы получаем, что в крайних столбцах доски ровно по x различных цветов. Нетрудно видеть, что в остальных столбцах доски должны встречаться только те цвета, которые встречаются в пересечении цветов крайних столбцов доски.

Будем последовательно перебирать два числа x и y, где x - количество используемых цветов в каждом из крайних столбцов, y - количество общих цветов в крайних столбцах. При этом ясно, что на x и y накладываются некоторые ограничения в зависимости от размера доски.  Будем считать ответ для каждой такой пары, а затем все их просуммируем. Тогда для каждой пары (x,y) имеем, что надо сначала выбрать (2x-y) цветов из k данных цветов, которые мы будем использовать при раскраске. Поэтому ответ для пары нужно сразу умножить на C(k,2x-y). Далее, выберем (x-y) уникальных цветов, которые будут использоваться в первом столбце. Ответ умножается еще на C(2x-y,x-y). Выбирая (x-y) уникальных цветов для последнего столбца, имеем еще одно умножение ответа на C(x,x-y). Теперь надо знать, сколько существует способов раскрасить n клеток в x цветов. Это несложная динамика (при этом цвета учитываются лексикографически, что означает, что если на доске есть цвета a и b, при этом a<b, то цвет a встречается раньше чем b), которая пересчитывается по формуле: d[i][j]=j*d[i-1][j]+d[i-1][j-1]. Домножаем ответ для пары на d[n][x] (раскрашиваем первый столбец) и потом еще раз на d[n][x] (раскрашиваем последний столбец). После этого в каждом из крайних столбцов можно сделать перестановку цветов, поэтому нужно ответ умножить на (x!)2. Теперь осталось домножить ответ на yn(m-2), так как цвета в средних столбиках могут быть любыми из пересечения.

Можно привести простой код, который проверяет нужные нам пары:

long long ans=0;

     

for (int y=0; y<=n; y++){

      long long cur=powmod(y,n*(m-2));

      for (int x=y; x<=n; x++)

      if (2*x-y<=k)

      {

            long long tek=cnk[2*x-y];

            tek*=cnn[2*x-y][x-y], tek%=mod;

            tek*=cnn[x][x-y], tek%=mod;

            tek*=d[n][x], tek%=mod;

            tek*=d[n][x], tek%=mod;

            tek*=f[x], tek%=mod;

            tek*=f[x], tek%=mod;

            tek*=cur;

            ans+=tek;

            ans%=mod;

      }

}

cout<<ans<<endl;

Здесь, cnk[i] – это предпросчитанное C(k,i), cnn[a][b] – C(a,b), d[n][x] – динамика как в разборе, f[x] – x!.

Заметим, что у некоторых участников возникли проблемы с тайм лимитом, однако если аккуратно предпросчитать все C(k,i) и помнить, что нам никогда не понадобится более чем 2000 цветов из предложенных 1000000, то проблем со временем быть не должно. Следует отметить, что авторское решение работало менее 200 мс, при предложенном тайм лимите в 5 секунд.

 Дивизион 1, задача E. Петя и прямоугольник

Обозначим длину максимального пути через S. Оценим число S, исходя из ограничений задачи. Раскрасим нашу доску в шахматную раскраску. Тогда получим, что любые две соседние клетки в максимальном пути имеют разный цвет. Из этого уже можем сделать какие-то ограничения на S. Действительно, к примеру, если у нас на доске получилось всего 4 белых клетки и 5 черных, при этом стартовая клетка белая и конечная белая, то очевидным образом длина пути не превосходит 7, так как белые и черные клетки должны чередоваться в пути. Таким образом, можем написать несложную функцию, которая считает максимальное теоретически возможное значение S (n, m – размеры доски, (sx, sy) – стартовая клетка, (fx, fy) – конечная клетка).

int fnd_ans(int n,int m,int sx,int sy,int fx,int fy){

      int col1=((sx+sy+1)%2); //цвет стартовой клетки

      int col2=((fx+fy+1)%2); //цвет конечной клетки

 

      int cntb=(n*m+1)/2; //количество черных клеток на доске

      int cntw=(n*m)/2; //количество белых клеток на доске

 

      //разбор случаев для нахождения ответов

      if (col1==1&&col2==1)

            return cntb*2-1;

      if (col1==1&&col2==0)

            return cntw*2;

      if (col1==0&&col2==1)

            return cntw*2;

      if (col1==0&&col2==0)

            return 2*cntw-1;

}

Оказывается, что для ограничений приведенных в задаче, максимальный теоретический ответ всегда достигается. Теперь посмотрим как считать этот ответ. Авторское решение подразумевало разбиение доски на части и после этого решение задачи для каждой из частей.

 

Разобьем доску на 5 частей, как показано на левом рисунке. При этом пока будем считать, что конфигурация начальной и конечной клеток будет именно такой. Тогда в каждой из частей попробуем построить самый длинный путь, проходящий именно в этой части. При этом для части 1 будем строить путь из правой верхней клетки в левую верхнюю. Для части 2 из левой нижней в правую верхнюю. Для части 3 из левой верхней в правую нижнюю, для части 4 из правой верхней в левую нижнюю и для части 5 из правой нижней в правую верхнюю. Пути, которые мы ищем в каждой части могут немножко меняться для других досок, однако при этом общая структура в целом сохраняется. После этого, видим, что у всех 5 частей всего 2 различных вида путей с учетом поворотов – из верхнего левого угла в правый нижний и из верхнего левого в правый верхний. Теперь можем сформулировать алгоритм.

1)     Делим доску на части

2)     В каждой из частей ищем длиннейший путь

3)     Проверяем подходит ли найденный путь под оптимальный ответ

4)     Поворачиваем/переворачиваем доску и переходим к шагу 1.

 

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

Данное решение проходит стресс-тест для всех досок с 4<=n,m<=20. Так как для таких досок рассматриваются все возможные варианты четности сторон каждой из частей, на которые делится вся доска, то можно сделать вывод, что решение работает для любых досок. Итоговая сложность -  O(n*m).

Разбор задач Codeforces Beta Round 85 (Div. 1 Only)
Разбор задач Codeforces Beta Round 85 (Div. 2 Only)
  • Проголосовать: нравится
  • 0
  • Проголосовать: не нравится

13 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

Е: да, в условии 5 запретов, похожих на условия корректности этого разбиения.


13 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится
Thanks for sharing the analysis. Waiting for the other problems, especially Div I problem C, please explain it throughly.
The pictures are invisible, please fix it.
Pretty nice problemsets, short and clear statements, I like it very much.
»
7 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

Can you please provide a proof that in Div 2 B, a line must cross the central square ?

»
7 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

In case, anybody wants a more detailed analysis of Div 2 C, I have written a post about it here.

»
6 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

worst editorial ever for Div2 D

  • »
    »
    5 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится

    I think so

    • »
      »
      »
      4 года назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится

      Yes I think the editorial for Div2 D Petya and Divisors, is not clear enough, So I will try to explain it a bit for anyone who comes after me.

      Since the elements are only in range 1 to 100000, the possible divisors will be in that range, we can create an array used[] , where used[j] will tell what was the last index when we encountered a X with j as one of its divisors.

      We can initialize used[] with -1.

      Now for an Xi, we can iterate over all its divisors, for a divisor d, we can check that if used[d]==-1 (not encountered this divisor until now) or used[d]<i-y (it was last encountered outside of [x-1,x-y] range), we need to count these cases. Also take care of cases when y==0.

      After that, we will update used[d]=idx

      code=> https://codeforces.me/contest/111/submission/82272791

»
3 года назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

Thanks. good editorial

»
3 месяца назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

112A — Petya and Strings I can't solve this problem. Does anyone help me?

»
2 месяца назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

just use python and then you can easily compare them by doing something like this: if s.lower > s1.lower: print(1)