2º Educkathon · Maratona de Programação

Bem-vindo ao
PyMaratona

Antes de começar, como devemos te chamar?

Seu progresso será salvo automaticamente neste dispositivo.

2º EDUCKATHON — MARATONA DE PROGRAMAÇÃO

Python para
Competição

Tudo que sua equipe precisa dominar antes da maratona — do zero ao avançado.

10
Módulos
60+
Exemplos de código
100%
Prático
Módulo 01 · Início

Fundamentos do Python

Por que Python é tão poderoso e como pensar nessa linguagem desde o primeiro código.

Por que Python para competições?

Python permite que você escreva menos código para resolver mais problemas. Em uma maratona, tempo é o recurso mais escasso — e Python respeita isso.

Sintaxe limpa

Lê quase como inglês. Menos erros de sintaxe, mais foco na lógica.

Biblioteca padrão

collections, math, itertools já resolvem metade dos problemas.

Tipagem dinâmica

Você não declara tipos. Python infere. Código mais rápido de escrever.

Interpretado

Teste imediato. Sem compilar. Ciclo de tentativa-erro mais veloz.

Seu primeiro programa

Em Python, cada linha é uma instrução. Sem chaves, sem ponto-e-vírgula. A indentação define os blocos.

# Isso é um comentário
print("Olá, Maratona!")

nome = "Equipe CEGLB"
pontos = 100
print(nome, "conquistou", pontos, "pontos")
saída
Olá, Maratona!
Equipe CEGLB conquistou 100 pontos

Variáveis e atribuição

Uma variável é um nome que aponta para um valor na memória. Em Python, você pode trocar o tipo a qualquer momento.

x = 10          # inteiro
y = 3.14       # float
ativo = True    # booleano
nome = "Ana"   # string

# Atribuição múltipla — muito útil em maratona!
a, b, c = 1, 2, 3

# Trocar valores sem variável auxiliar
a, b = b, a
print(a, b)  # 2 1

Operadores aritméticos

OperadorOperaçãoExemploResultado
+Soma7 + 310
-Subtração7 - 34
*Multiplicação7 * 321
/Divisão real7 / 23.5
//Divisão inteira7 // 23
%Resto (módulo)7 % 31
**Potência2 ** 101024
💡
Em maratonas, % (módulo) aparece o tempo todo. Serve para verificar paridade, ciclos, e hashing. Domine-o.

Números inteiros em Python

Python suporta inteiros de tamanho arbitrário. Você pode calcular fatoriais enormes sem overflow — algo impossível em C ou Java por padrão.

print(2 ** 100)
# 1267650600228229401496703205376
# Sem overflow. Sem erro. Python simplesmente calcula.
Módulo 02 · Dados

Tipos de Dados

Python tem tipos ricos. Saber quando usar cada um é diferencial em competições.

int, float, bool

# Inteiros
n = 42
print(type(n))   # <class 'int'>

# Floats
pi = 3.14159
e = 2.718

# Booleanos — são subclasse de int!
print(True + True)   # 2
print(True * 5)       # 5
print(False + 10)     # 10

# Conversões
int("42")       # 42
float("3.14")   # 3.14
str(100)         # "100"

Operadores de comparação e lógicos

# Comparação
5 == 5   # True
5 != 3   # True
5 >= 5   # True

# Comparação encadeada — exclusivo do Python!
1 < x < 10   # equivale a 1 < x AND x < 10

# Lógicos
True and False   # False
True or False    # True
not True         # False
💡
Comparação encadeada 0 <= n < 100 é muito comum em maratonas para validar intervalos.

Identidade e pertencimento

# in — verifica se elemento existe
3 in [1, 2, 3]         # True
"a" in "maratona"       # True
"k" not in "python"    # True

# is — verifica identidade (mesmo objeto na memória)
x = None
x is None    # True — use is para None, não ==

Valores que avaliam como False

Em Python, vários valores se comportam como False em condicionais. Saber isso evita bugs:

# Todos avaliam como False:
False, 0, 0.0, "", [], {}, set(), None

# Exemplo prático
lista = []
if not lista:
    print("lista vazia!")  # Mais pythonico que len(lista) == 0
Módulo 03 · Lógica

Estruturas de Controle

if, for, while — a espinha dorsal de qualquer algoritmo. Dominar esses três é dominar a lógica computacional.

Condicional if / elif / else

nota = 78

if nota >= 90:
    print("A")
elif nota >= 70:
    print("B")
elif nota >= 50:
    print("C")
else:
    print("Reprovado")
saída
B

Expressão condicional em uma linha (ternário):

resultado = "par" if n % 2 == 0 else "ímpar"

Laço for

O for em Python itera sobre qualquer sequência. Não é um contador como em C — é um iterador real.

# Iterar sobre lista
for fruta in ["maçã", "banana", "uva"]:
    print(fruta)

# range(n) — de 0 até n-1
for i in range(5):         # 0 1 2 3 4
    print(i, end=" ")

# range(start, stop, step)
for i in range(0, 20, 2):   # pares de 0 a 18
    print(i, end=" ")

# enumerate — índice + valor
for i, v in enumerate(["a", "b", "c"]):
    print(i, v)   # 0 a, 1 b, 2 c

Laço while

n = 1
while n <= 10:
    print(n, end=" ")
    n += 1

# break — interrompe o laço
while True:
    entrada = input("Digite 0 para sair: ")
    if entrada == "0":
        break

# continue — pula para próxima iteração
for i in range(10):
    if i % 2 == 0:
        continue
    print(i, end=" ")  # 1 3 5 7 9

For com else diferencial

Python permite else no final de um for. O bloco else só executa se o loop completou sem break. Muito útil para busca.

# Verificar se número é primo
def eh_primo(n):
    if n < 2:
        return False
    for i in range(2, int(n**0.5) + 1):
        if n % i == 0:
            return False   # divisor encontrado
    return True  # nenhum divisor → primo
Módulo 04 · Organização

Funções

Funções transformam código repetitivo em blocos reutilizáveis. Em maratona, organizar bem as funções economiza 30 minutos.

Definindo funções

def somar(a, b):
    return a + b

print(somar(3, 7))   # 10

# Parâmetros com valor padrão
def potencia(base, exp=2):
    return base ** exp

potencia(4)      # 16 — usa exp=2
potencia(2, 10)  # 1024

Retorno múltiplo

Python retorna múltiplos valores naturalmente — na verdade retorna uma tupla, que é desempacotada automaticamente.

def dividir(a, b):
    return a // b, a % b  # quociente, resto

q, r = dividir(17, 5)
print(q, r)   # 3 2

Funções com *args e **kwargs

# *args — aceita qualquer número de argumentos
def soma_total(*nums):
    return sum(nums)

soma_total(1, 2, 3, 4, 5)   # 15

# **kwargs — argumentos nomeados
def perfil(**info):
    for k, v in info.items():
        print(k, ":", v)

perfil(nome="Ana", idade=17)

Funções lambda maratona

Lambda cria funções anônimas em uma linha. Essencial para usar com sorted(), map() e filter().

# Ordenar lista de tuplas pelo segundo elemento
pares = [("Ana", 85), ("Bia", 72), ("Carlos", 91)]
pares.sort(key=lambda x: x[1], reverse=True)
print(pares)
# [('Carlos', 91), ('Ana', 85), ('Bia', 72)]

# Filtrar ímpares
impares = list(filter(lambda x: x % 2 != 0, range(10)))
# [1, 3, 5, 7, 9]

Recursão

# Fatorial recursivo
def fatorial(n):
    if n <= 1:
        return 1
    return n * fatorial(n - 1)

# Fibonacci
def fib(n):
    if n <= 1:
        return n
    return fib(n-1) + fib(n-2)
Recursão simples é lenta para n grande. Em maratona, use memoização (próximo módulo) para evitar recalcular valores.
Módulo 05 · Sequências

Listas e Tuplas

Listas são a estrutura mais versátil do Python. Tuplas são listas imutáveis e mais rápidas.

Criando e manipulando listas

nums = [3, 1, 4, 1, 5, 9, 2, 6]

# Acesso por índice
nums[0]    # 3 (primeiro)
nums[-1]   # 6 (último)

# Fatiamento [start:stop:step]
nums[2:5]     # [4, 1, 5]
nums[::-1]    # lista invertida
nums[1::2]    # elementos nos índices 1,3,5,7

# Modificação
nums.append(5)        # adiciona ao final
nums.insert(0, 99)    # insere no índice 0
nums.remove(1)        # remove primeira ocorrência de 1
nums.pop()            # remove e retorna o último
nums.pop(0)           # remove e retorna o índice 0
nums.sort()           # ordena in-place
nums.sort(reverse=True)  # ordena decrescente

List Comprehension poderoso

Uma das ferramentas mais poderosas do Python. Cria listas em uma linha com total legibilidade.

# Forma tradicional
quadrados = []
for i in range(10):
    quadrados.append(i**2)

# Com list comprehension — uma linha!
quadrados = [i**2 for i in range(10)]

# Com condição (filtro)
pares = [i for i in range(20) if i % 2 == 0]

# Aninhado — matriz 3x3
matriz = [[i*3+j for j in range(3)] for i in range(3)]

# Transformação
maiusculas = [s.upper() for s in ["ana", "bia"]]

Funções nativas com listas

nums = [3, 1, 4, 1, 5, 9]

len(nums)       # 6 — tamanho
sum(nums)       # 23 — soma
max(nums)       # 9 — máximo
min(nums)       # 1 — mínimo
sorted(nums)    # [1,1,3,4,5,9] — retorna nova lista
nums.count(1)   # 2 — conta ocorrências
nums.index(5)   # 4 — índice do elemento

# zip — combinar duas listas
nomes = ["Ana", "Bia", "Carlos"]
notas = [90, 85, 78]
for n, nota in zip(nomes, notas):
    print(n, "->", nota)

Tuplas

# Tupla — imutável
ponto = (3, 7)
x, y = ponto   # desempacotamento

# Uso principal em maratona: chave de dicionário
grid = {}
grid[(0, 0)] = "inicio"
grid[(3, 4)] = "fim"

# Nomeados com namedtuple
from collections import namedtuple
Ponto = namedtuple("Ponto", ["x", "y"])
p = Ponto(3, 7)
print(p.x, p.y)   # 3 7
Módulo 06 · Hash

Dicionários e Sets

Dicionários resolvem contagem, agrupamento e mapeamento em O(1). Sets eliminam duplicatas e fazem operações de conjuntos. Ambos são armas em maratonas.

Dicionários

# Criação
aluno = {"nome": "Ana", "idade": 17, "nota": 9.5}

# Acesso
aluno["nome"]            # "Ana"
aluno.get("turma", "N/A")  # "N/A" — sem KeyError

# Modificação
aluno["nota"] = 10
aluno["turma"] = "3A"
del aluno["idade"]

# Iteração
for chave in aluno:           # itera pelas chaves
    print(chave)
for k, v in aluno.items():   # chave e valor
    print(k, "->", v)

# Verificar existência
"nome" in aluno    # True

defaultdict e Counter essencial

Duas estruturas do módulo collections que economizam linhas e evitam bugs:

from collections import defaultdict, Counter

# defaultdict — nunca dá KeyError
grafo = defaultdict(list)
grafo["A"].append("B")   # sem precisar inicializar
grafo["A"].append("C")

# Counter — conta elementos automaticamente
texto = "banana"
c = Counter(texto)
print(c)
# Counter({'a': 3, 'n': 2, 'b': 1})

c.most_common(2)  # [('a', 3), ('n', 2)]

# Contar frequência de palavras
palavras = "o rato roeu a roupa do rei de roma".split()
freq = Counter(palavras)

Sets (conjuntos)

a = {1, 2, 3, 4}
b = {3, 4, 5, 6}

a | b    # {1,2,3,4,5,6} — união
a & b    # {3,4} — interseção
a - b    # {1,2} — diferença
a ^ b    # {1,2,5,6} — diferença simétrica

# Verificar subconjunto
{1,2}.issubset(a)    # True

# Remover duplicatas de uma lista
lista = [1, 2, 2, 3, 3, 3]
sem_dup = list(set(lista))  # [1, 2, 3]

# Busca em O(1) — muito mais rápido que lista!
validos = {1, 5, 10, 50, 100}
if x in validos:   # rápido!
    print("válido")
Módulo 07 · Texto

Strings Avançado

Problemas de string são comuns em maratonas. Python tem as melhores ferramentas nativas para isso.

Métodos essenciais de string

s = "  Python é Poderoso  "

s.strip()          # "Python é Poderoso" — remove espaços
s.lower()          # "  python é poderoso  "
s.upper()          # "  PYTHON É PODEROSO  "
s.replace("é", "é muito")
s.split()          # ['Python', 'é', 'Poderoso']
s.split(",")       # divide por vírgula
s.startswith("Py")  # False (tem espaço)
s.strip().startswith("Py")  # True
s.find("é")        # índice da primeira ocorrência
s.count("o")       # conta ocorrências
",".join(["a", "b", "c"])  # "a,b,c"

f-strings moderno

A forma mais elegante de formatar strings em Python:

nome = "Ana"
nota = 9.756

print(f"Aluna: {nome}, nota: {nota:.2f}")
# Aluna: Ana, nota: 9.76

print(f"{'centrado':^20}")   # centraliza em 20 chars
print(f"{'esquerda':<20}")  # alinha à esquerda
print(f"{1000000:,}")      # 1,000,000 com separador
print(f"{255:08b}")        # 11111111 em binário

Strings como sequências

s = "maratona"

# Fatiamento funciona igual a listas
s[0]       # "m"
s[-1]      # "a"
s[::-1]    # "anotoram" — string invertida!
s[:4]      # "mara"

# Verificar palíndromo
def palindromo(s):
    s = s.lower().replace(" ", "")
    return s == s[::-1]

print(palindromo("Ame a ema"))  # True

Expressões regulares (básico)

import re

texto = "Ligue para 79 99988-7766 ou 11 3456-7890"

# Encontrar todos os telefones
fones = re.findall(r'\d{2}\s\d{4,5}-\d{4}', texto)
# ['79 99988-7766', '11 3456-7890']

# Substituir
limpo = re.sub(r'\s+', ' ', texto)  # colapsa espaços

# Verificar padrão
if re.match(r'^\d+$', "12345"):
    print("só números")
Módulo 08 · I/O

Entrada e Saída

Em maratonas, 80% dos bugs são na leitura da entrada. Dominar input/output é obrigatório.

Leitura padrão (stdin)

# Ler uma linha inteira
linha = input()                # retorna string

# Ler um inteiro
n = int(input())

# Ler vários inteiros na mesma linha
a, b, c = map(int, input().split())

# Ler lista de inteiros
nums = list(map(int, input().split()))

# Ler N linhas em loop
n = int(input())
dados = []
for _ in range(n):
    dados.append(int(input()))
O _ como nome de variável é convenção para "não vou usar esse valor". Comum em loops onde só importa a contagem.

Leitura rápida com sys.stdin performance

Para entradas muito grandes, sys.stdin é até 5x mais rápido que input():

import sys
input = sys.stdin.readline   # sobrescreve input()

# A partir daqui, input() usa sys.stdin
n = int(input())
nums = list(map(int, input().split()))

# Ou ler tudo de uma vez
data = sys.stdin.read().split()
# data é uma lista com todos os tokens

Saída formatada

# Vários valores separados por espaço
print(1, 2, 3)              # 1 2 3
print(1, 2, 3, sep=",")     # 1,2,3
print(1, 2, 3, sep="\n")    # um por linha
print("a", end="")          # sem newline

# Imprimir lista sem colchetes
nums = [1, 2, 3, 4]
print(*nums)               # 1 2 3 4
print(*nums, sep="\n")    # um por linha

# Escrita rápida em massa
resultados = [f"Caso {i}: {v}" for i, v in enumerate(dados)]
print("\n".join(resultados))

Template de solução para maratona

⚡ Use isso como ponto de partida em todo problema
import sys
from collections import defaultdict, Counter, deque
from math import gcd, lcm, sqrt, log, inf
import heapq

input = sys.stdin.readline

def solve():
    n = int(input())
    # Sua solução aqui
    print(n)

# Para múltiplos casos de teste:
t = int(input())
for _ in range(t):
    solve()
Módulo 09 · Poder

Bibliotecas Essenciais

Python vem com baterias inclusas. Essas bibliotecas resolvem em uma linha o que outros levariam 50.

math — operações matemáticas

import math

math.gcd(12, 8)        # 4 — máximo divisor comum
math.lcm(4, 6)         # 12 — mínimo múltiplo comum
math.sqrt(144)         # 12.0
math.floor(3.7)        # 3 — arredonda pra baixo
math.ceil(3.2)         # 4 — arredonda pra cima
math.factorial(10)     # 3628800
math.log(1000, 10)     # 3.0 — log base 10
math.inf               # infinito positivo
math.pi                # 3.14159265...

collections — estruturas de dados

from collections import deque, Counter, defaultdict

# deque — fila de duas pontas (O(1) em ambas)
fila = deque()
fila.append(1)       # adiciona à direita
fila.appendleft(0)   # adiciona à esquerda
fila.pop()           # remove da direita
fila.popleft()       # remove da esquerda (use em BFS!)

# Counter aritmético
c1 = Counter("aab")
c2 = Counter("bbc")
print(c1 + c2)  # Counter({'b': 3, 'a': 2, 'c': 1})
print(c1 - c2)  # Counter({'a': 2}) — apenas positivos

heapq — fila de prioridade algoritmos

import heapq

h = [5, 2, 8, 1, 9]
heapq.heapify(h)           # transforma em heap
heapq.heappush(h, 3)       # insere
print(heapq.heappop(h))    # 1 — menor elemento

# Heap máximo: negate values
max_h = [-x for x in [5, 2, 8]]
heapq.heapify(max_h)
print(-heapq.heappop(max_h))  # 8

# N maiores/menores sem sort completo
heapq.nlargest(3, [5,2,8,1,9])   # [9, 8, 5]
heapq.nsmallest(3, [5,2,8,1,9]) # [1, 2, 5]

itertools — combinatória

from itertools import permutations, combinations, product

# Permutações de [1,2,3]
list(permutations([1,2,3]))
# [(1,2,3),(1,3,2),(2,1,3),(2,3,1),(3,1,2),(3,2,1)]

# Combinações de 2 elementos
list(combinations([1,2,3,4], 2))
# [(1,2),(1,3),(1,4),(2,3),(2,4),(3,4)]

# Produto cartesiano
list(product(["A","B"], [1,2]))
# [('A',1),('A',2),('B',1),('B',2)]

functools — utilidades funcionais

from functools import lru_cache, reduce

# lru_cache — memoização automática!
@lru_cache(maxsize=None)
def fib(n):
    if n <= 1:
        return n
    return fib(n-1) + fib(n-2)

fib(100)  # instantâneo! Sem lru_cache seria lentíssimo

# reduce — acumular
reduce(lambda x, y: x*y, [1,2,3,4,5])  # 120
💡
lru_cache é uma das armas secretas do Python em maratonas. Qualquer função recursiva com subproblemas repetidos fica O(n) com um único decorator.
Módulo 10 · Final Boss

Táticas de Maratona

Algoritmos clássicos que aparecem sempre. Conheça os padrões e você vai reconhecer o problema antes de terminar de ler.

BFS — Busca em Largura

Use BFS para menor caminho em grafos não ponderados, problemas de grade, e quando precisar de distância mínima.

from collections import deque

def bfs(grafo, inicio):
    visitado = {inicio}
    fila = deque([(inicio, 0)])  # (nó, distância)
    
    while fila:
        no, dist = fila.popleft()
        print(no, "dist:", dist)
        
        for vizinho in grafo[no]:
            if vizinho not in visitado:
                visitado.add(vizinho)
                fila.append((vizinho, dist+1))

DFS — Busca em Profundidade

def dfs(grafo, no, visitado=None):
    if visitado is None:
        visitado = set()
    visitado.add(no)
    print(no)
    for vizinho in grafo[no]:
        if vizinho not in visitado:
            dfs(grafo, vizinho, visitado)

# DFS iterativo (evita recursion limit)
def dfs_iter(grafo, inicio):
    visitado = {inicio}
    pilha = [inicio]
    while pilha:
        no = pilha.pop()
        print(no)
        for vizinho in grafo[no]:
            if vizinho not in visitado:
                visitado.add(vizinho)
                pilha.append(vizinho)
Python tem limite de recursão (~1000 por padrão). Para grafos grandes, use DFS iterativo ou adicione sys.setrecursionlimit(100000) no início.

Ordenação personalizada

# Ordenar por múltiplos critérios
dados = [("Ana", 17, 90), ("Bia", 17, 85), ("Carlos", 16, 95)]

# Ordena por idade ASC, depois por nota DESC
dados.sort(key=lambda x: (x[1], -x[2]))

# Ordenação estável por múltiplas chaves
from functools import cmp_to_key
def comparar(a, b):
    if a[0] != b[0]: return a[0] - b[0]
    return b[1] - a[1]
dados.sort(key=cmp_to_key(comparar))

Busca binária

# Implementação manual
def busca_binaria(arr, alvo):
    esq, dir = 0, len(arr) - 1
    while esq <= dir:
        meio = (esq + dir) // 2
        if arr[meio] == alvo:
            return meio
        elif arr[meio] < alvo:
            esq = meio + 1
        else:
            dir = meio - 1
    return -1  # não encontrado

# Com bisect (biblioteca pronta!)
import bisect
arr = [1, 3, 5, 7, 9]
bisect.bisect_left(arr, 5)   # 2 — índice de inserção
bisect.insort(arr, 6)        # insere mantendo ordenação

Checklist final de maratona

✓ Leu o problema?

Leia 2 vezes. Identifique: entrada, saída, restrições, casos extremos.

✓ Complexidade?

n < 10⁶ → O(n log n) OK. n < 10³ → O(n²) OK. n < 20 → força bruta.

✓ sys.stdin?

Sempre use no início. Para n grande é imprescindível.

✓ Casos extremos?

n=0, n=1, lista vazia, número negativo, divisão por zero.

✓ Tipo de retorno?

A saída pede int? float? String específica? Não confunda.

✓ lru_cache?

Recursão com subproblemas? Adicione @lru_cache e economize segundos.

🏆 Você chegou ao fim do curso

Revisita os módulos sempre que precisar. O segredo de uma maratona não é saber tudo — é saber onde encontrar quando precisar. Boa sorte, equipe!

Módulos do Curso
01 Fundamentos 02 Tipos de Dados 03 Controle de Fluxo 04 Funções 05 Listas e Tuplas 06 Dicionários e Sets 07 Strings Avançado 08 Entrada e Saída 09 Bibliotecas Essenciais 10 Táticas de Maratona