Comentário NOIC TFC 2020

TFC 2020

Para conferir a prova na íntegra, clique aqui.

Transporte

Comentário escrito por Vitor Veiga

Conhecimento prévio necessário:

Dadas duas capacidades, máxima e mínima, de um trem L e M, queremos achar uma nova capacidade máxima N tal que esta vale 30% de L, e dizer se ela é maior que M. Além disso, devemos remover as casas decimais de N para realizar a comparação.

Sendo assim, basta calcular N = 0.3 * L e depois checar se N < M. Se for, retornamos 0, se não, retornamos M. Note que poderiamos declarar N como uma variável do tipo double e depois retirar as casas decimais utilizando a função floor(), porém, se declararmos N como um int, o C++ automaticamente realiza a aproximação para o maior inteiro menor que o número decimal resultante da operação.

Clique aqui para conferir o código

Diária

Comentário escrito por Arthur Lobo

Conhecimento prévio necessário:

Já que a soma de todos os K é menor do que 10^5, podemos criar uma lista que guardará quanto custa a diária em cada um dos dias. Sendo Sk o a soma de todos os K, vamos criar um array D_1, D_2, ..., D_{Sk}, em que D_i guardará quanto custa a diária do dia i.

Para montar esse array, digamos que a soma dos K até o blocos de dias i-1 é Sk e já montamos a lista até esse bloco (até o dia Sk). Então se o bloco de dias i tem tamanho K e preço P, então temos que fazer com que todos os valores de Sk+1 até Sk+K sejam P, ou seja, D_j = P, para Sk+1 \le j \le Sk+K, e em seguida aumentamos a soma dos K fazendo Sk+= K.

Agora que já montamos a lista de quanto custa cada diária, sabemos que para responder uma consulta X,Y, ou seja, que pergunta qual vai ser o valor total das diárias se ficar hospedado do dia X até o dia Y, devemos computar o valor de D_X + D_{X+1} + ... + D_Y. Agora vamos para as parciais.

Vale ressaltar que se deve usar "long long" ao invés de "int" para computar a soma dos valores de D. O único motivo pelo qual existem algumas parciais com P \le 10^3 é para caso alguém esqueça de usar long long.

Parcial 1 (Q \le 100)

Como nessa parcial nos temos poucas consultas, podemos responder cada uma delas em O(N).Para uma consulta X,Y, primeiro inicializamos uma variável "int soma = 0", e então computamos a soma das diárias fazendo um for com i indo de X até Y, e fazendo soma+= D_i. No final, a variável soma guardará D_X + D_{X+1} + ... + D_Y.

A complexidade ficará O(Sk) por consulta, então no total é O(Sk*Q), que é o suficiente.

Parcial 2 (X = 1)

Nessa parcial, o X da consulta é sempre 1, então só nos importamos com o Y, já que o que vamos querer computar é D_1 + D_2 + ... + D_Y, ou seja, a soma de um prefixo da lista.

Para responder qual a soma de cada prefixo, vamos criar outro array P_1, P_2, ..., P_{Sk}, em que P_i vai guardar a soma das diárias até i, ou seja, P_i = D_1 + D_2 + ... + D_i, mas como computar esse novo array?

Perceba que sempre temos que P_i = P_{i-1} + D_i, pois P_{i-1} guardá D_1 + D_2 + ... + D_{i-1} e depois só adicionamos D_i. Portanto, começamos com P_0 = 0, e faremos um for com i indo de 1 até Sk (nessa ordem), para cada i, faremos P_i = P_{i-1}+D_i, pois quando estamos em i, já calculamos P_{i-1}.

Agora, para responder uma consulta 1,Y (já que X = 1), basta imprimirmos P_Y. A complexidade fica O(Sk) de pré-processamento e O(1) para cada consulta, portando no total fica O(Sk+Q).

Parcial 3 e 4 (Sem restrições adicionais)

Diferente da parcial anterior, agora cada consulta vai pedir qual é o valor de D_X + D_{X+1} + ... + D_Y, mas como podemos usar o array P para nos ajudar a computar esse valor de forma rápida? Tente imaginar como saber a soma de cada prefixo pode nos ajudar a descobrir a soma desejada.

P_Y é D_1 + D_{2} + ... + D_Y, o problema é que queremos apenas D_X + D_{X+1} + ... + D_Y. Podemos dividir essa soma que forma P_Y em 2 partes: (D_1 + D_{2} + D_{X-1}) + (D_X + D_{X+1} + ... + D_Y), mas nós queremos apenas essa segunda parte. Perceba que essa primeira parte é exatamente P_{X-1}, então para conseguirmos a soma desejada, podemos fazer P_Y-P_{X-1}, já que, como visto a cima, vai sobrar apenas a segunda parte da maneira que dividimos o P_Y.

Agora, para responder uma consulta X,Y, basta imprimirmos P_Y-P_{X-1}. A complexidade fica O(Sk) de pré-processamento e O(1) para cada consulta, portando no total fica O(Sk+Q), que é o suficiente.

Clique aqui para conferir o código

Robô

Comentário escrito por Caique Paiva e Enzo Dantas

Conhecimento prévio necessário:

Primeiro, vamos fazer a subtask 3, ou seja, N, M, L \le 100, K \le 10^4. Nesse caso, a ideia é simples! Basta fazer uma matriz N \times M, e, supondo que estamos em (X, Y), que é inicializada com (X_i, Y_i), salvamos uma variável auxiliar con = 0, que vai contar a quantidade de obstáculos que já batemos, e dependendo da direção atual, fazemos o seguinte:

  • Se estamos andando para a direita, então, se (X+1, Y) for um obstáculo, então mudamos a direção para baixo, e fazemos con++. Se não, faça X++.
  • Se estamos andando para baixo, então, se (X, Y+1) for um obstáculo, então mudamos a direção para esquerda, e fazemos con++ . Se não, faça Y++.
  • Se estamos andando para a esquerda, então, se (X-1, Y) for um obstáculo, então mudamos a direção para cima, e fazemos con++. Se não, faça X--.
  • Se estamos andando para cima, então, se (X, Y-1) for um obstáculo, então mudamos a direção para direita, e fazemos con++. Se não, faça Y--.

Se con == L, então pare de rodar o código, então, a resposta será (X, Y).

Perfeito! Agora, vamos melhorar essa solução. Primeiro, para o problema original, temos que N, M \le 10^5, e então não conseguimos nem construir a matriz, pois se não vai dar MLE (Erro no Limite de Memória), então, vamos criar um vetor de set <int> linhas[N] e um vetor de set <int> colunas [M]. Então, se tem um obstáculo em (i, j), fazemos linhas[i].insert(j) e colunas[j].insert(i), e então, a ideia é ver que, se estamos na casa (X, Y), então, nós vamos andar até chegamos na próxima parede, e aí viramos, ou seja, não precisamos andar as casa uma de cada vez. Logo, vamos mudar nossas condições que fizemos acima. Suponha que estamos em (X, Y), que é inicializada com (X_i, Y_i), temos que

  • Se estamos andando para a direita, então, procuramos no set linhas[X] qual é o primeiro obstáculo depois da casa (X, Y) e na mesma linha que X, ou seja, qual é o primeiro Z em linhas[X] com Z > Y, e paramos de andar em (X, Z-1), e então basta fazer Y = Z-1, e con++, pois batemos em uma parede, e então viramos para baixo (Para achar esse valor, basta olhar o ponteiro linhas[X].upper_bound(Y)).
  • As ideias são analogas para os outros casos. Se estamos andando para baixo, então, procuramos no set colunas[Y] qual é o primeiro obstáculo depois de (X, Y), ou seja, qual é o primeiro Z em colunas[Y] tal que Z > X, e paramos de andar em (Z-1, Y), e então basta fazer  X = Z-1, con++ e virar para esquerda (Para achar esse valor, basta olhar o ponteiro colunas[Y].upper_bound(X)).
  • Se estamos andando para a esquerda, então, procuramos no set linhas[X] qual é o primeiro obstáculo depois de (X, Y), ou seja, qual é o primeiro Z em linhas[X] tal que Z < Y, e paramos de andar em (Z+1, Y), e então, faça X = Z+1, con++ e virar para cima (Para achar esse valor, basta olhar o ponteiro linhas[X].upper_bound(Y) e voltar o ponteiro uma vez).
  • Se estamos andando para cima, então, procuramos no set colunas[Y] qual é o primeiro obstáculo depois de (X, Y), ou seja, qual é o primeiro Z em colunas[Y] tal que Z < X, e paramos de antar em X, Z+1), e então, faça Y = Z+1, con++, e vire para a direita (Para achar esse valor, basta olhar o ponteiro colunas[Y].upper_bound(X), e voltar o ponteiro uma vez).

Clique aqui para conferir o código

Sobrinhos

Comentário escrito por Enzo Dantas

Conhecimento prévio necessário:

Primeiramente vamos criar um array com os restos de cada pessoa. Mais especificamente, vamos criar um array v e dizer que v_i = X_i \mathbin{\%} Y_i, tal que % é a função que retorna o resto da divisão entre X_i e Y_i. Em seguida vamos ordenar esse array em ordem crescente usando a função sort. Finalmente, a resposta será o k-ésimo elemento desse array.

Clique aqui para conferir o código