Comentário OBI – Fase 3 – Programação 2

por

Comentário por Frederico Bulhões

Cinco

Para resolvermos essa questão devemos observar uma coisa:

  • O número inicial dado não é divisível por cinco
    • Então para que haja resposta é necessário que a troca ocorra entre um número igual a 0 ou 5 e o número da ultima posição, pois então o novo número terminará em 0 ou 5, tornando-o divisível por cinc0.

Como N \leq 1000, podemos então fazer um algoritmo em O(n^2) da seguinte forma:

Para todas as posições entre  1 e N checamos se o dígito daquela posição é 0 ou 5, caso seja, trocamos ele com o ultímo, e comparamos com a resposta computada até agora, caso maior ele se torna a resposta, e depois trocamos de volta o dígito atual com o ultímo.

Na implementação a seguir é ussado a função max do C++, que compara lexicográficamente dois vetores passados e retorna o maior.

Código para melhor entendimento:


#include <bits/stdc++.h>
using namespace std;
int main()
{
int n;
cin >> n;
vector<int> v(n), ans(n);
for (int i = 0; i < n; i++)
cin >> v[i];
bool ok = false;
for (int i = 0; i < n; i++) {
if (v[i] == 5 or v[i] == 0) {
swap(v[i], v[n-1]);
ans = max(ans, v);
swap(v[i], v[n-1]);
ok = true;
}
}
if (!ok)
cout << -1 << "\n";
else {
for (int x : ans)
cout << x << " ";
cout << "\n";
}
}

view raw

cinco.cpp

hosted with ❤ by GitHub

Muro

Essa é uma questão de combinatória, em que podemos usar programação dinâmica como meio de resolver.

Vamos tentar achar uma recorrência para reolver o caso N:

Pelo desenho fornecido na questão pode-se ver que caso tenhamos um muro de tamanho N podemos preênche-lo da seguinte maneira:

  • Colocando dois blocos de tamanho 1 próximos a um muro de tamanho N-1\Rightarrow F_{N-1}
  • Colocando dois blocos, um L e o outro um quadrado, de quatro formas diferentes, próximos a um muro de tamanho N-2\Rightarrow 4\cdot F_{N-2}
  • Colocado dois blocos L próximos de duas formas diferentes, próximos a um muro de tamanho N-3\Rightarrow 2\cdot F_{N-3}

Então ficamos com a recorrência F_N = F_{N-1} + 4\cdot F_{N-2} + 2\cdot F_{N-3} \mod{10^9+7}. (Sempre tirando o módulo, como pedido na questão)

Os casos base são dados na questão, F_0 = 1, F_1 = 1, F_2 = 5.

Para computar F_N podemos usar programação dinâmica, de forma recursiva, salvando o resultado computado agora para não termos que computa-lo de novo, reduzindo a complexidade de O(3^N) para O(N), ou podemos fazer de forma iterativa, fazendo um for de 3 até o N desejado, computado o valor atual usando a recorrência dada e os 3 valores anteriores.

Segue o código para melhor entandimento, (implementado de forma iterativa):


#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
const int maxn = 10101;
ll dp[maxn];
int main()
{
int n;
cin >> n;
dp[0] = 1, dp[1] = 1, dp[2] = 5;
for (int i = 3; i <= n; i++) {
dp[i] = dp[i-1] + 4*dp[i-2] + 2*dp[i-3];
dp[i] %= mod;
}
cout << dp[n] << "\n";
}

view raw

muro.cpp

hosted with ❤ by GitHub

Baldes

Para resolvermos esse problema, devemos ter uma noção da estrutura Segment Tree, caso não a conheça recomendo que leia esse TUTORIAL para ter uma noção do que vamos falar agora.

Primeiro devemos perceber que para cada balde só importa o menor e o maior valor, e os outros são irrelevantes para a resposta, pois não faz sentido colocar uma bola com o outra de outro balde quando poderíamos pegar uma menor ou maior.

Nesse problema podemos em cada nó da Seg Tree, que representa os baldes entre L e R, guardar três valores:

  • Valor máximo das bolas entre os baldes L e R, incluso.
  • Valor mínimo das bola entre os baldes L e R, incluso.
  • Máximo entre:
    • Diferença máxima entre bolas de baldes entre L a M e de baldes entre M+1 e R, (sendo M ponto do meio entre L e R);
    • Diferença máxima do filho da esquerda, bloco que representa L a M
    • Diferênca máxima do filho da direita, bloco que representa M+1 a R

Podemos então fazer as queries  updates de acordo com a questão, cada uma em O(log(N)), somando u tempo total de O(N + Q log(N))

Recomendo que leia a implementação com cuidado, e caso tenha dúvidas consulte um tutorial de Segment Tree

Código para melhor entendimento:


#include <bits/stdc++.h>
using namespace std;
struct Node
{
int mini, maxi, ans;
};
Node join(Node const& a, Node const& b)
{
return {min(a.mini, b.mini),
max(a.maxi, b.maxi),
max({a.ans, b.ans, abs(a.maxi-b.mini), abs(b.maxi-a.mini)})};
}
const int maxn = 101010;
int v[maxn];
Node tree[maxn*4];
void build(int no, int l, int r)
{
if (l == r) {
tree[no].mini = tree[no].maxi = v[l];
return;
}
int m = (l+r)/2;
build(no*2+1, l, m);
build(no*2+2, m+1, r);
tree[no] = join(tree[no*2+1], tree[no*2+2]);
}
Node get(int no, int l, int r, int a, int b)
{
if (a <= l and r <= b) return tree[no];
int m = (l+r)/2;
if (b <= m) return get(no*2+1, l, m, a, b);
if (a > m) return get(no*2+2, m+1, r, a, b);
return join(get(no*2+1, l, m, a, b),
get(no*2+2, m+1, r, a, b));
}
void upd(int no, int l, int r, int pos, int val)
{
if (l == r) {
tree[no].mini = min(tree[no].mini, val);
tree[no].maxi = max(tree[no].maxi, val);
return;
}
int m = (l+r)/2;
if (pos <= m) upd(no*2+1, l, m, pos, val);
else upd(no*2+2, m+1, r, pos, val);
}
int main()
{
ios::sync_with_with_stdio(false), cin.tie(0);
int n, q;
cin >> n >> q;
for (int i = 1; i <= n; i++)
cin >> v[i];
build(0, 1, n);
while (q–) {
int op;
cin >> op;
if (op == 1) {
int val, pos;
cin >> val >> pos;
upd(0, 1, n, pos, val);
}
else {
int l, r;
cin >> l >> r;
cout << get(0, 1, n, l, r).ans << "\n";
}
}
}

view raw

baldes.cpp

hosted with ❤ by GitHub

Mancha

Para resolver esse problema devemos fazer a seguinte observação:

  • Caso d_{manhattan}(x, y) > d(x, y)” /></span><script type='math/tex'>d_{manhattan}(x, y) > d(x, y)</script> então para algum ponto <span class='MathJax_Preview'><img data-recalc-dims= na mesma linha ou coluna que o x, é verdade que d_{manhattan}(x,z) > d(x, z)” /></span><script type='math/tex'>d_{manhattan}(x,z) > d(x, z)</script>.
<ul>
<li>Com isso podemos fazer uma busca, em cada linha ou coluna, vendo se existe um buraco nela. Sendo um buraco um quadrado que não faz parte da mancha, mas que para cima e pra baixo, ou pra esquerda e direita, tenha algum ponto que faz parte da mancha. Pois a distância entre esses pontos, será maior que a distância de <em>manhattan,</em>  pois teremos que dar uma volta.</li>
</ul>
</li>
</ul>
<p>Então podemos escrever uma solução em <span class='MathJax_Preview'><img data-recalc-dims= para resolver esse problema, passando por cada linha e cada coluna checando essa condição em O(n).

    Nessa implementação checamosse a soma de pontos que são da mancha entre o primeiro e ultimo daquela linha/coluna é igual a distância entre o primeiro e o último. Caso sim para toadas as linhas e colunas a resposta é sim. Senão a resposta é não.

    Código para melhor entendimento:


    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 1010;
    char m[maxn][maxn];
    int main()
    {
    int n;
    cin >> n;
    for (int i = 0; i < n; i++)
    for (int j = 0; j < n; j++)
    cin >> m[i][j];
    for (int i = 0; i < n; i++) {
    int ini = n, fim, cnt = 0;
    for (int j = 0; j < n; j++) {
    if (m[i][j] == '*') {
    if (ini == n) {
    ini = j;
    }
    cnt++;
    fim = j;
    }
    }
    if (ini != n and cnt != fim-ini+1) {
    cout << "N\n";
    return 0;
    }
    }
    for (int i = 0; i < n; i++) {
    int ini = n, fim, cnt = 0;
    for (int j = 0; j < n; j++) {
    if (m[j][i] == '*') {
    if (ini == n) {
    ini = j;
    }
    cnt++;
    fim = j;
    }
    }
    if (ini != n and cnt != fim-ini+1) {
    cout << "N\n";
    return 0;
    }
    }
    cout << "S\n";
    }

    view raw

    mancha.cpp

    hosted with ❤ by GitHub

    Bolas

    Para resolver esse problema podemos usar o Prinípio da Casa dos Pombos. Caso tenhamos um valor com mais de 4 ocorrências, então obrigatoriamente vamos ter que colocar uma dessas bola do lado da outra igual. Para resolver esse problema bastachecar essa condição, pois caso ela ocorra, só pode ocorrer com uma bola de um valor.

    O código resultante tem complexidade de O(1).

    Código para melhor entendimento:


    #include <bits/stdc++.h>
    using namespace std;
    int freq[10];
    int main()
    {
    for (int i = 0; i < 8; i++) {
    int x;
    cin >> x;
    freq[x]++;
    }
    for (int i = 0; i <= 9; i++) {
    if (freq[i] > 4) {
    cout << "N\n";
    return 0;
    }
    }
    cout << "S\n";
    }

    view raw

    bolas.cpp

    hosted with ❤ by GitHub

Comentários

Comente