Resolução de Problemas AtCoder Beginner Contest 292: De Manipulação de Strings a Teoria de Grafos

Problema A: Conversão para Maiúsculas

O desafio conisste em receber uma string composta por letras minúsculas e transformá-la integralmente em letras maiúsculas. Em C++, isso pode ser resolvido iterando sobre a string e utilizando a função toupper() ou manipulando diretamente os valores ASCII.

#include <iostream>
#include <string>
#include <cctype>

using namespace std;

void process_uppercase() {
    string texto;
    cin >> texto;
    for (char &caractere : texto) {
        caractere = toupper(caractere);
    }
    cout << texto << endl;
}

int main() {
    process_uppercase();
    return 0;
}

Problema B: Controle de Cartões em Partida de Futebol

Neste problema, devemos gerenciar o estado de jogadores com base em cartões amarelos e vermelhos. As regras são simples: um cartão vermelho expulsa o jogador imediatamente, enquanto dois cartões amarelos acumulam o mesmo efeito. Utilizamos um vetor para rastrear a "pontuação de penalidade" de cada jogador.

#include <iostream>
#include <vector>

using namespace std;

void monitor_penalties() {
    int total_jogadores, total_eventos;
    cin >> total_jogadores >> total_eventos;
    
    vector<int> status(total_jogadores + 1, 0);
    
    while (total_eventos--) {
        int tipo, id;
        cin >> tipo >> id;
        
        if (tipo == 1) {
            status[id] += 1;
        } else if (tipo == 2) {
            status[id] += 2;
        } else {
            if (status[id] >= 2) cout << "Yes" << endl;
            else cout << "No" << endl;
        }
    }
}

int main() {
    monitor_penalties();
    return 0;
}

Problema C: Decomposição de Inteiros AB + CD = N

O objetivo é encontrar o número de quádruplas $(A, B, C, D)$ tais que $A \times B + C \times D = N$. A estratégia otimizada envolve pré-processar a quantidade de divisores para cada número até $N$. Se $X = A \times B$, o número de pares $(A, B)$ é igual ao número de divisores de $X$.

#include <iostream>
#include <vector>

using namespace std;

typedef long long ll;

void count_quadruplets() {
    int n;
    cin >> n;
    
    vector<ll> divisores(n + 1, 0);
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; i * j <= n; ++j) {
            divisores[i * j]++;
        }
    }
    
    ll total_combinacoes = 0;
    for (int k = 1; k < n; ++k) {
        total_combinacoes += (divisores[k] * divisores[n - k]);
    }
    
    cout << total_combinacoes << endl;
}

int main() {
    count_quadruplets();
    return 0;
}

Problema D: Verificação de Componentes Unicíclicos

Neste cenário, um grafo é dado e precisamos veriifcar se, em cada componente conexo, o número de vértices é exatamente igual ao número de arestas. A estrutura de dados Disjoint Set Union (DSU) é ideal para agrupar os componentes e contabilizar as propriedades de cada um.

#include <iostream>
#include <vector>
#include <numeric>

using namespace std;

struct DSU {
    vector<int> pai;
    vector<int> v_cnt, e_cnt;
    DSU(int n) {
        pai.resize(n + 1);
        iota(pai.begin(), pai.end(), 0);
        v_cnt.assign(n + 1, 1);
        e_cnt.assign(n + 1, 0);
    }
    int buscar(int i) {
        if (pai[i] == i) return i;
        return pai[i] = buscar(pai[i]);
    }
    void unir(int i, int j) {
        int raiz_i = buscar(i);
        int raiz_j = buscar(j);
        if (raiz_i != raiz_j) {
            pai[raiz_i] = raiz_j;
            v_cnt[raiz_j] += v_cnt[raiz_i];
            e_cnt[raiz_j] += e_cnt[raiz_i] + 1;
        } else {
            e_cnt[raiz_i]++;
        }
    }
};

int main() {
    int n, m;
    cin >> n >> m;
    DSU dsu(n);
    for (int i = 0; i < m; ++i) {
        int u, v;
        cin >> u >> v;
        dsu.unir(u, v);
    }
    
    bool valido = true;
    for (int i = 1; i <= n; ++i) {
        if (dsu.pai[i] == i) {
            if (dsu.v_cnt[i] != dsu.e_cnt[i]) {
                valido = false;
                break;
            }
        }
    }
    
    cout << (valido ? "Yes" : "No") << endl;
    return 0;
}

Problema E: Adição de Arestas para Transitividade

O problema pede para encontrar quantas arestas direcionadas podem ser adicionadas de modo que, se houver um caminho entre $u$ e $v$, exista uma aresta direta $u \to v$. O resultado final é a soma de todos os nós alcançáveis a partir de cada nó (excluindo o próprio nó), subtraindo o número de arestas originais.

#include <iostream>
#include <vector>
#include <queue>

using namespace std;

int calcular_alcancaveis(int inicial, int n, const vector<vector<int>>& adj) {
    vector<bool> visitado(n + 1, false);
    queue<int> fila;
    fila.push(inicial);
    visitado[inicial] = true;
    int contador = 0;
    
    while (!fila.empty()) {
        int atual = fila.front();
        fila.pop();
        
        for (int vizinho : adj[atual]) {
            if (!visitado[vizinho]) {
                visitado[vizinho] = true;
                contador++;
                fila.push(vizinho);
            }
        }
    }
    return contador;
}

int main() {
    int n, m;
    cin >> n >> m;
    vector<vector<int>> adj(n + 1);
    for (int i = 0; i < m; ++i) {
        int u, v;
        cin >> u >> v;
        adj[u].push_back(v);
    }
    
    long long total_pares = 0;
    for (int i = 1; i <= n; ++i) {
        total_pares += calcular_alcancaveis(i, n, adj);
    }
    
    cout << total_pares - m << endl;
    return 0;
}

Tags: AtCoder C++ GraphTheory algorithms DSU

Publicado em 7-14 09:20