Soluções Técnicas e Algoritmos do AtCoder Beginner Contest 353

Problema A - Buildings

O objetivo é identificar o índice do primeiro edifício cuja altura seja estritamente maior que a do edifício inicial da sequência. Caso nenhum edifício satisfaça essa condição, o algoritmo deve retornar -1. A abordagem processa os dados de entrada em tempo linear, mantendo a altura de referência e verificando cada valor subsequente.

#include <iostream>

using namespace std;

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    
    int totalBuildings;
    if (!(cin >> totalBuildings)) return 0;
    
    int referenceHeight;
    cin >> referenceHeight;
    
    int targetIndex = -1;
    for (int i = 2; i <= totalBuildings; ++i) {
        int currentHeight;
        cin >> currentHeight;
        if (targetIndex == -1 && currentHeight > referenceHeight) {
            targetIndex = i;
        }
    }
    
    cout << targetIndex << "\n";
    return 0;
}

Problema B - AtCoder Amusement Park

Devemos alocar grupos de amigos em atrações que possuem uma restrição máxima de capacidade K. A solução consiste em simular o processo de alocação: mantemos um contador da ocupação atual da atração. Ao adicionar um novo grupo, se a capacidade for excedida, incrementamos o número de atrações necessárias e reiniciamos a ocupação com o tamanho do grupo atual.

#include <iostream>

using namespace std;

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    
    int numGroups, maxCapacity;
    cin >> numGroups >> maxCapacity;
    
    int attractionsUsed = 1;
    int currentOccupancy = 0;
    
    for (int i = 0; i < numGroups; ++i) {
        int groupSize;
        cin >> groupSize;
        
        if (currentOccupancy + groupSize <= maxCapacity) {
            currentOccupancy += groupSize;
        } else {
            attractionsUsed++;
            currentOccupancy = groupSize;
        }
    }
    
    cout << attractionsUsed << "\n";
    return 0;
}

Problema C - Sigma Problem

Para calcular o somatório de (A_i + A_j) % 10^8 para todos os pares, podemos inicialmenet assumir que não há restrição de módulo. Nesse cenário, cada elemento é somado N - 1 vezes. Para corrigir o valor e aplicar o módulo, ordenamos o array e utilizamos a técnica de dois ponteiros. Para cada elemento à direita, contamos quantos elementos à esquerda formam uma soma maior ou igual a 10^8, subtraindo o excesso correspondente do total acumulado.

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    
    int n;
    cin >> n;
    vector<long long> values(n);
    long long totalSum = 0;
    const long long MOD_VAL = 100000000LL;
    
    for (int i = 0; i < n; ++i) {
        cin >> values[i];
        totalSum += values[i] * (n - 1);
    }
    
    sort(values.begin(), values.end());
    
    int leftPtr = 0;
    long long penalty = 0;
    
    for (int right = 0; right < n; ++right) {
        while (leftPtr < right && values[leftPtr] + values[right] >= MOD_VAL) {
            leftPtr++;
        }
        penalty += leftPtr;
    }
    
    cout << totalSum - (penalty * MOD_VAL) << "\n";
    return 0;
}

Problema D - Another Sigma Problem

A concatenação de dois números A e B equivale matematicamente a A * 10^(len(B)) + B. A estratégia envolve manter um mapa de frequências com o comprimento das strings de cada número. Ao iterar pelo array, calculamos a contribuição de cada elemento atuando tanto como a parte direita (multiplicado pelo número de elementos anteriores) quanto como a parte esquerda (multiplicado por potências de 10 baseadas nos comprimentos dos elementos subsequentes).

#include <iostream>
#include <vector>
#include <string>
#include <map>

using namespace std;

const long long MOD = 998244353;

long long power(long long base, long long exp) {
    long long res = 1;
    base %= MOD;
    while (exp > 0) {
        if (exp % 2 == 1) res = (res * base) % MOD;
        base = (base * base) % MOD;
        exp /= 2;
    }
    return res;
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    
    int n;
    cin >> n;
    vector<long long> sequence(n);
    map<int, int> lengthCounts;
    
    for (int i = 0; i < n; ++i) {
        cin >> sequence[i];
        lengthCounts[to_string(sequence[i]).length()]++;
    }
    
    long long total = 0;
    for (int i = 0; i < n; ++i) {
        total = (total + sequence[i] * i) % MOD;
        
        int currentLen = to_string(sequence[i]).length();
        lengthCounts[currentLen]--;
        
        for (auto const& [len, count] : lengthCounts) {
            if (count > 0) {
                long long multiplier = power(10, len);
                long long contribution = (sequence[i] * multiplier) % MOD;
                contribution = (contribution * count) % MOD;
                total = (total + contribution) % MOD;
            }
        }
    }
    
    cout << total << "\n";
    return 0;
}

Problema E - Yet Another Sigma Problem

Para determinar a soma dos prefixos comuns mais longos (LCP) entre todos os pares de strings, empregamos uma estrutura de dados Trie. Durante a inserção de cada palavra, incrementamos um contador em cada nó visitado. A contribuição total de um nó específico para o resultado final é dada pela combinação de strings que compartilham aquele prefixo, calculada através da fórmula C * (C - 1) / 2, onde C é a frequência de passagem pelo nó.

#include <iostream>
#include <string>
#include <vector>

using namespace std;

struct TrieNode {
    int passCount;
    int children[26];
    TrieNode() {
        passCount = 0;
        fill(begin(children), end(children), 0);
    }
};

const int MAX_NODES = 1000005;
TrieNode trie[MAX_NODES];
int nodeCounter = 0;

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    
    int n;
    cin >> n;
    
    for (int i = 0; i < n; ++i) {
        string word;
        cin >> word;
        int current = 0;
        for (char ch : word) {
            int idx = ch - 'a';
            if (!trie[current].children[idx]) {
                trie[current].children[idx] = ++nodeCounter;
            }
            current = trie[current].children[idx];
            trie[current].passCount++;
        }
    }
    
    long long totalLCP = 0;
    for (int i = 1; i <= nodeCounter; ++i) {
        long long c = trie[i].passCount;
        totalLCP += c * (c - 1) / 2;
    }
    
    cout << totalLCP << "\n";
    return 0;
}

Problema F - Tile Distance

A mecânica de movimentação neste grid varia de acordo com o parâmetro K. Para K=1, a solução é a tradicional distância de Manhattan. Quando K=2, o custo de transição entre blocos exige uma fórmula ajustada. Para K >= 3, a rota diagonal torna-se a mais eficiente; modelamos isso rotacionando o sistema de coordenadas em 45 graus. O algoritmo avalia o custo mínimo para alcançar um bloco em formato de L a partir das posições inicial e final, combinando esses custos com a distância entre os próprios blocos.

#include <iostream>
#include <vector>
#include <array>
#include <cmath>
#include <algorithm>

using namespace std;

vector<array<long long, 3>> getAdjacentBlocks(long long x, long long y, long long k) {
    x += k; 
    y += k;
    vector<array<long long, 3>> blocks;
    
    if (((x / k) + (y / k)) % 2 != 0) {
        blocks.push_back({x / k, y / k, 0});
        return blocks;
    }
    
    blocks.push_back({x / k, y / k + 1, k - (y % k)});
    blocks.push_back({x / k, y / k - 1, (y % k) + 1});
    blocks.push_back({x / k - 1, y / k, (x % k) + 1});
    blocks.push_back({x / k + 1, y / k, k - (x % k)});
    
    return blocks;
}

long long calculateBlockDistance(long long x1, long long y1, long long x2, long long y2, long long k) {
    long long dx = abs(x1 - x2);
    long long dy = abs(y1 - y2);
    
    if (k == 2) {
        return dx + dy + abs(dx - dy) / 2;
    }
    
    long long rx1 = x1 + y1, ry1 = y1 - x1;
    long long rx2 = x2 + y2, ry2 = y2 - x2;
    
    return abs(rx1 - rx2) + abs(ry1 - ry2);
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    
    long long k, sx, sy, tx, ty;
    cin >> k >> sx >> sy >> tx >> ty;
    
    long long minCost = abs(sx - tx) + abs(sy - ty);
    
    if (k == 1) {
        cout << minCost << "\n";
        return 0;
    }
    
    auto startBlocks = getAdjacentBlocks(sx, sy, k);
    auto targetBlocks = getAdjacentBlocks(tx, ty, k);
    
    for (const auto& start : startBlocks) {
        for (const auto& target : targetBlocks) {
            long long currentCost = start[2] + target[2] + 
                calculateBlockDistance(start[0], start[1], target[0], target[1], k);
            minCost = min(minCost, currentCost);
        }
    }
    
    cout << minCost << "\n";
    return 0;
}

Tags: AtCoder Algoritmos C++ Trie geometria-computacional

Publicado em 7-9 04:22