// Verificador de "Qué secciones limitan la matrícula": verificador ENTRADA ESPERADA SALIDA.
// En ESPERADA está solo k. Se comprueba que A y su lista de secciones estén bien escritas, que esa lista
// sea exactamente N(A) y que |A| − c(N(A)) = n − k. Termina con 0 si todo está bien, y con 1 y un mensaje si no.
#include <cstddef>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>

namespace {

[[noreturn]] void reject(const std::string& reason) {
    std::cout << reason << '\n';
    std::exit(1);
}

// Convierte una palabra en número sin riesgo de overflow: solo dígitos, y a lo sumo 9.
bool read_number(std::ifstream& output, std::size_t& value, const std::string& what) {
    std::string word;
    if (!(output >> word)) return false;
    if (word.empty() || word.size() > 9) reject(what + " inválido: " + word);
    value = 0;
    for (char c : word) {
        if (c < '0' || c > '9') reject(what + " inválido: " + word);
        value = value * 10 + static_cast<std::size_t>(c - '0');
    }
    return true;
}

}  // namespace

int main(int argc, char* argv[]) {
    if (argc != 4) reject("uso: verificador ENTRADA ESPERADA SALIDA");
    std::ifstream input(argv[1]);
    std::ifstream expected(argv[2]);
    std::ifstream output(argv[3]);

    std::size_t n = 0;
    std::size_t m = 0;
    input >> n >> m;
    std::vector<long long> quota(m);
    for (long long& q : quota) input >> q;
    std::vector<std::vector<std::size_t>> options(n);
    for (auto& list : options) {
        std::size_t count = 0;
        input >> count;
        list.resize(count);
        for (std::size_t& section : list) input >> section;
    }
    std::size_t best = 0;
    expected >> best;

    std::size_t k = 0;
    if (!read_number(output, k, "k")) reject("falta k");
    if (k != best) reject("k esperado: " + std::to_string(best) + ", salió " + std::to_string(k));

    std::size_t a = 0;
    if (!read_number(output, a, "a")) reject("falta la cantidad de estudiantes del grupo");
    if (a > n) reject("el grupo tiene más estudiantes que n");
    std::vector<bool> in_group(n, false);
    std::vector<bool> needed(m, false);
    for (std::size_t i = 0; i < a; i++) {
        std::size_t student = 0;
        if (!read_number(output, student, "estudiante")) reject("faltan estudiantes en el grupo");
        if (student < 1 || student > n) reject("estudiante fuera de rango: " + std::to_string(student));
        if (in_group[student - 1]) reject("el estudiante " + std::to_string(student) + " aparece dos veces");
        in_group[student - 1] = true;
        for (std::size_t section : options[student - 1]) needed[section - 1] = true;
    }

    std::size_t b = 0;
    if (!read_number(output, b, "b")) reject("falta la cantidad de secciones");
    if (b > m) reject("hay más secciones que m");
    std::vector<bool> listed(m, false);
    for (std::size_t j = 0; j < b; j++) {
        std::size_t section = 0;
        if (!read_number(output, section, "sección")) reject("faltan secciones");
        if (section < 1 || section > m) reject("sección fuera de rango: " + std::to_string(section));
        if (listed[section - 1]) reject("la sección " + std::to_string(section) + " aparece dos veces");
        listed[section - 1] = true;
    }
    std::string extra;
    if (output >> extra) reject("sobra texto después de las secciones: " + extra);

    long long capacity = 0;
    for (std::size_t j = 0; j < m; j++) {
        if (needed[j] && !listed[j]) reject("falta la sección " + std::to_string(j + 1) + ", que alguien del grupo puede usar");
        if (!needed[j] && listed[j]) reject("sobra la sección " + std::to_string(j + 1) + ": nadie del grupo puede usarla");
        if (listed[j]) capacity += quota[j];
    }
    long long deficit = static_cast<long long>(a) - capacity;
    long long missing = static_cast<long long>(n) - static_cast<long long>(k);
    if (deficit != missing) {
        reject("el grupo explica " + std::to_string(deficit) + " y faltan " + std::to_string(missing));
    }
    return 0;
}
