#include <algorithm>
#include <array>
#include <chrono>
#include <climits>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <deque>
#include <fstream>
#include <iostream>
#include <numeric>
#include <optional>
#include <random>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>

using Clause = std::vector<int>;

#include "known_tours.inc"

struct Layout {
    int width = 0;
    int block_start = 0;
    int base = 0;
    int fixed_block = 0;
    std::vector<int> fixed_bits;
    int modulus = 0;
};

struct Instance {
    int nvars = 0;
    std::vector<Clause> clauses;
    Layout layout;
    std::vector<std::pair<int, int>> arcs;      // 1-based variable -> (source block, dest block)
    std::vector<char> normal_arc;
    int nblocks = 0;
    std::vector<std::pair<int, int>> und_edges;
    std::vector<std::vector<int>> incident;
    std::unordered_map<long long, int> arc_var;
};

static bool read_dimacs(const char* path, Instance& I) {
    std::ifstream in(path);
    if (!in) return false;
    std::string tok;
    while (in >> tok) {
        if (tok == "c") {
            std::string rest;
            std::getline(in, rest);
        } else if (tok == "p") {
            std::string fmt;
            int nclauses = 0;
            in >> fmt >> I.nvars >> nclauses;
            I.clauses.reserve(nclauses);
        } else {
            int lit = std::stoi(tok);
            Clause c;
            while (lit != 0) {
                c.push_back(lit);
                in >> lit;
            }
            if (!c.empty()) I.clauses.push_back(std::move(c));
        }
    }
    return I.nvars > 0 && !I.clauses.empty();
}

static bool infer_layout(Instance& I) {
    const Clause& first = I.clauses.front();
    int first_min = I.nvars + 1;
    for (int l : first) first_min = std::min(first_min, std::abs(l));

    std::vector<int> units;
    for (const auto& c : I.clauses)
        if (c.size() == 1) units.push_back(c[0]);
    if (units.empty()) return false;

    std::vector<int> vals;
    vals.reserve(units.size());
    for (int u : units) vals.push_back(std::abs(u));
    std::sort(vals.begin(), vals.end());

    std::vector<int> best, cur;
    for (int v : vals) {
        if (cur.empty() || v == cur.back() + 1) {
            cur.push_back(v);
        } else {
            if (cur.size() > best.size()) best = cur;
            cur.assign(1, v);
        }
    }
    if (cur.size() > best.size()) best = cur;
    int w = (int)best.size();
    if (!(w == 7 || w == 8 || w == 10)) return false;

    Layout L;
    L.width = w;
    L.block_start = (w == 8) ? first_min : first_min - 2;
    L.base = L.block_start - 1;
    if (L.base <= 0 || L.block_start <= 0) return false;
    L.fixed_block = (best.front() - L.block_start) / w;
    L.fixed_bits.assign(w, 0);
    for (int u : units) {
        int v = std::abs(u);
        if (best.front() <= v && v <= best.back()) {
            L.fixed_bits[v - best.front()] = (u > 0) ? 1 : 0;
        }
    }
    L.modulus = (w == 7) ? 60 : (w == 8 ? 105 : 420);
    I.layout = std::move(L);
    return true;
}

static int block_of(const Layout& L, int v) {
    return (v - L.block_start) / L.width;
}
static int off_of(const Layout& L, int v) {
    return (v - L.block_start) % L.width;
}

static std::optional<std::pair<int, int>> infer_arc_from_guard(const std::vector<Clause>& g, const Layout& L) {
    for (int i = 0; i + 3 < (int)g.size(); ++i) {
        std::vector<int> hs;
        for (int k = i; k < i + 4; ++k) {
            for (int lit : g[k]) {
                int v = std::abs(lit);
                if (v >= L.block_start) hs.push_back(v);
            }
        }
        std::sort(hs.begin(), hs.end());
        hs.erase(std::unique(hs.begin(), hs.end()), hs.end());
        if (hs.size() != 3) continue;
        std::array<std::vector<int>, 3> offs;
        std::array<int, 3> blocks;
        int nb = 0;
        for (int v : hs) {
            int b = block_of(L, v), o = off_of(L, v);
            int idx = -1;
            for (int j = 0; j < nb; ++j)
                if (blocks[j] == b) idx = j;
            if (idx < 0) {
                idx = nb;
                blocks[nb++] = b;
            }
            offs[idx].push_back(o);
        }
        for (int j = 0; j < nb; ++j) {
            std::sort(offs[j].begin(), offs[j].end());
            if (offs[j].size() == 2 && offs[j][0] == 0 && offs[j][1] == 1) {
                int other = -1;
                for (int k = 0; k < nb; ++k)
                    if (k != j) other = k;
                if (other >= 0) return std::make_pair(blocks[j], blocks[other]);
            }
        }
    }
    return std::nullopt;
}

static bool extract_graph(Instance& I) {
    if (!infer_layout(I)) return false;
    const Layout& L = I.layout;
    if (L.base >= I.nvars) return false;

    std::vector<std::vector<Clause>> guarded(L.base + 1);
    for (const auto& c : I.clauses) {
        int low = 0, lows = 0;
        bool has_high = false;
        for (int lit : c) {
            int v = std::abs(lit);
            if (lit < 0 && v <= L.base) {
                low = v;
                lows++;
            }
            if (v >= L.block_start) has_high = true;
        }
        if (lows == 1 && has_high && (int)guarded[low].size() < 40) guarded[low].push_back(c);
    }

    I.arcs.assign(L.base + 1, {-1, -1});
    I.normal_arc.assign(L.base + 1, 0);
    for (int e = 1; e <= L.base; ++e) {
        auto r = infer_arc_from_guard(guarded[e], L);
        if (r) {
            I.arcs[e] = *r;
            I.normal_arc[e] = 1;
        }
    }
    for (int e = 1; e + 1 <= L.base; e += 2) {
        if (I.arcs[e].first >= 0 && I.arcs[e + 1].first < 0) {
            auto [s, d] = I.arcs[e];
            I.arcs[e + 1] = {d, s};
        } else if (I.arcs[e + 1].first >= 0 && I.arcs[e].first < 0) {
            auto [s, d] = I.arcs[e + 1];
            I.arcs[e] = {d, s};
        }
    }
    int maxb = -1;
    for (int e = 1; e <= L.base; ++e) {
        if (I.arcs[e].first < 0) return false;
        maxb = std::max(maxb, std::max(I.arcs[e].first, I.arcs[e].second));
    }
    I.nblocks = maxb + 1;
    if (I.nblocks <= 0 || I.nblocks > 20000) return false;

    I.und_edges.clear();
    I.arc_var.reserve((size_t)L.base * 2);
    auto key = [&](int a, int b) -> long long { return (long long)a * I.nblocks + b; };
    for (int e = 1; e <= L.base; ++e) {
        auto [s, d] = I.arcs[e];
        I.arc_var[key(s, d)] = e;
    }
    for (int e = 1; e + 1 <= L.base; e += 2) {
        auto [a, b] = I.arcs[e];
        if (a == b) continue;
        I.und_edges.push_back({a, b});
    }
    I.incident.assign(I.nblocks, {});
    for (int i = 0; i < (int)I.und_edges.size(); ++i) {
        auto [a, b] = I.und_edges[i];
        if (a < 0 || b < 0 || a >= I.nblocks || b >= I.nblocks) return false;
        I.incident[a].push_back(i);
        I.incident[b].push_back(i);
    }
    return true;
}

struct RNG {
    uint64_t x;
    explicit RNG(uint64_t seed) : x(seed ? seed : 1) {}
    uint64_t next() {
        uint64_t z = (x += 0x9e3779b97f4a7c15ULL);
        z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ULL;
        z = (z ^ (z >> 27)) * 0x94d049bb133111ebULL;
        return z ^ (z >> 31);
    }
    int randint(int n) { return (int)(next() % (uint64_t)n); }
    double unit() { return (next() >> 11) * (1.0 / 9007199254740992.0); }
};

static bool random_2factor(const Instance& I, uint64_t seed, std::vector<char>& selected) {
    int n = I.nblocks, m = (int)I.und_edges.size();
    RNG rng(seed);
    std::vector<int8_t> state(m, -1);
    std::vector<int> sel(n, 0), undec(n);
    std::deque<int> q;
    for (int v = 0; v < n; ++v) {
        undec[v] = (int)I.incident[v].size();
        q.push_back(v);
    }

    auto set_edge = [&](int e, int val, std::vector<int8_t>& st, std::vector<int>& se,
                        std::vector<int>& un, std::deque<int>& qu) -> bool {
        if (st[e] != -1) return st[e] == val;
        st[e] = (int8_t)val;
        auto [a, b] = I.und_edges[e];
        for (int v : {a, b}) {
            un[v]--;
            if (val) se[v]++;
            if (se[v] > 2 || se[v] + un[v] < 2) return false;
            qu.push_back(v);
        }
        return true;
    };

    auto propagate = [&](std::vector<int8_t>& st, std::vector<int>& se,
                         std::vector<int>& un, std::deque<int>& qu) -> bool {
        while (!qu.empty()) {
            int v = qu.front();
            qu.pop_front();
            if (se[v] == 2) {
                for (int e : I.incident[v])
                    if (st[e] == -1 && !set_edge(e, 0, st, se, un, qu)) return false;
            } else if (se[v] + un[v] == 2) {
                for (int e : I.incident[v])
                    if (st[e] == -1 && !set_edge(e, 1, st, se, un, qu)) return false;
            }
        }
        return true;
    };

    if (!propagate(state, sel, undec, q)) return false;
    int undec_edges = 0;
    for (int8_t x : state)
        if (x == -1) undec_edges++;

    while (undec_edges > 0) {
        int best = -1;
        std::tuple<int, int, uint64_t> bestkey{99, 999, UINT64_MAX};
        for (int e = 0; e < m; ++e) {
            if (state[e] != -1) continue;
            auto [a, b] = I.und_edges[e];
            auto key = std::make_tuple(std::min(2 - sel[a], 2 - sel[b]), undec[a] + undec[b], rng.next());
            if (key < bestkey) {
                bestkey = key;
                best = e;
            }
        }
        if (best < 0) break;
        int val = (rng.unit() < 0.58) ? 1 : 0;

        auto st0 = state;
        auto se0 = sel;
        auto un0 = undec;
        auto q0 = q;
        bool ok = set_edge(best, val, state, sel, undec, q) && propagate(state, sel, undec, q);
        if (!ok) {
            state = std::move(st0);
            sel = std::move(se0);
            undec = std::move(un0);
            q = std::move(q0);
            ok = set_edge(best, 1 - val, state, sel, undec, q) && propagate(state, sel, undec, q);
            if (!ok) return false;
        }
        undec_edges = 0;
        for (int8_t x : state)
            if (x == -1) undec_edges++;
    }
    selected.assign(m, 0);
    for (int v = 0; v < n; ++v)
        if (sel[v] != 2) return false;
    for (int e = 0; e < m; ++e) {
        if (state[e] == 1) selected[e] = 1;
        else if (state[e] == -1) return false;
    }
    return true;
}

static bool cycles_from_selected(const Instance& I, const std::vector<char>& selected,
                                 std::vector<std::vector<int>>& cycles) {
    int n = I.nblocks;
    std::vector<std::vector<int>> nadj(n);
    for (int e = 0; e < (int)selected.size(); ++e) {
        if (!selected[e]) continue;
        auto [a, b] = I.und_edges[e];
        nadj[a].push_back(b);
        nadj[b].push_back(a);
    }
    for (int v = 0; v < n; ++v)
        if (nadj[v].size() != 2) return false;
    std::vector<char> seen(n, 0);
    cycles.clear();
    for (int s = 0; s < n; ++s) {
        if (seen[s]) continue;
        std::vector<int> cyc;
        int cur = s, prev = -1;
        while (!seen[cur]) {
            seen[cur] = 1;
            cyc.push_back(cur);
            int nxt = (nadj[cur][0] != prev) ? nadj[cur][0] : nadj[cur][1];
            prev = cur;
            cur = nxt;
        }
        cycles.push_back(std::move(cyc));
    }
    return true;
}

static std::vector<int> step_bits(const Layout& L, const std::vector<int>& x) {
    std::vector<int> out;
    int i = 0;
    if (L.width == 7 || L.width == 10) {
        int a = x[i], b = x[i + 1];
        out.push_back(1 - a);
        out.push_back(a ^ b);
        i += 2;
    }
    {
        int a = x[i], b = x[i + 1];
        out.push_back(b);
        out.push_back(a ^ b);
        i += 2;
    }
    {
        int a = x[i], b = x[i + 1], c = x[i + 2];
        out.push_back((!a && !c) ? 1 : 0);
        out.push_back(a ^ b);
        out.push_back(a & b);
        i += 3;
    }
    if (i < L.width) {
        int a = x[i], b = x[i + 1], c = x[i + 2];
        out.push_back(c);
        out.push_back(a);
        out.push_back(b ^ c);
        i += 3;
    }
    return out;
}

static bool residue_ok(const Instance& I, const std::vector<std::vector<int>>& cycles) {
    int target = I.nblocks % I.layout.modulus;
    for (const auto& c : cycles) {
        bool has_root = std::find(c.begin(), c.end(), I.layout.fixed_block) != c.end();
        int want = has_root ? target : 0;
        if ((int)c.size() % I.layout.modulus != want) return false;
    }
    return true;
}

static std::pair<int, int> residue_score(const Instance& I, const std::vector<std::vector<int>>& cycles) {
    int target = I.nblocks % I.layout.modulus;
    int bad = 0, dist = 0;
    for (const auto& c : cycles) {
        bool has_root = std::find(c.begin(), c.end(), I.layout.fixed_block) != c.end();
        int want = has_root ? target : 0;
        int r = (int)c.size() % I.layout.modulus;
        if (r != want) {
            bad++;
            int d = (r - want + I.layout.modulus) % I.layout.modulus;
            dist += std::min(d, I.layout.modulus - d);
        }
    }
    return {bad, dist};
}

static bool build_candidate_model(const Instance& I, const std::vector<std::vector<int>>& cycles,
                                  int root_dir, std::vector<int8_t>& assign) {
    const Layout& L = I.layout;
    assign.assign(I.nvars + 1, 0);
    for (int v = 1; v <= L.base; ++v) assign[v] = -1;

    std::vector<std::vector<int>> labels(I.nblocks, std::vector<int>(L.width, -1));
    auto key = [&](int a, int b) -> long long { return (long long)a * I.nblocks + b; };

    for (const auto& raw : cycles) {
        bool has_root = std::find(raw.begin(), raw.end(), L.fixed_block) != raw.end();
        std::vector<int> cyc;
        if (has_root) {
            int p = (int)(std::find(raw.begin(), raw.end(), L.fixed_block) - raw.begin());
            int len = (int)raw.size();
            cyc.reserve(len);
            for (int k = 0; k < len; ++k) {
                int idx = (p + root_dir * k) % len;
                if (idx < 0) idx += len;
                cyc.push_back(raw[idx]);
            }
        } else {
            cyc = raw;
        }
        std::vector<int> bits = L.fixed_bits;
        for (int i = 0; i < (int)cyc.size(); ++i) {
            int u = cyc[i], v = cyc[(i + 1) % cyc.size()];
            auto it = I.arc_var.find(key(u, v));
            if (it == I.arc_var.end()) return false;
            assign[it->second] = 1;
            if (labels[u][0] != -1 && labels[u] != bits) return false;
            labels[u] = bits;
            bits = step_bits(L, bits);
        }
    }

    for (int b = 0; b < I.nblocks; ++b) {
        if (labels[b][0] == -1) return false;
        for (int o = 0; o < L.width; ++o) {
            int var = L.block_start + b * L.width + o;
            if (var >= 1 && var <= I.nvars) assign[var] = labels[b][o] ? 1 : -1;
        }
    }
    return true;
}

static bool propagate_and_check(const Instance& I, std::vector<int8_t>& a) {
    bool changed = true;
    while (changed) {
        changed = false;
        for (const auto& c : I.clauses) {
            bool sat = false;
            int un = 0, last = 0;
            for (int lit : c) {
                int v = std::abs(lit);
                int8_t val = a[v];
                if (val == 0) {
                    un++;
                    last = lit;
                } else if ((lit > 0 && val > 0) || (lit < 0 && val < 0)) {
                    sat = true;
                    break;
                }
            }
            if (sat) continue;
            if (un == 0) return false;
            if (un == 1) {
                int v = std::abs(last);
                int8_t need = (last > 0) ? 1 : -1;
                if (a[v] != 0 && a[v] != need) return false;
                if (a[v] == 0) {
                    a[v] = need;
                    changed = true;
                }
            }
        }
    }
    for (int v = 1; v <= I.nvars; ++v)
        if (a[v] == 0) a[v] = -1;
    for (const auto& c : I.clauses) {
        bool sat = false;
        for (int lit : c) {
            int8_t val = a[std::abs(lit)];
            if ((lit > 0 && val > 0) || (lit < 0 && val < 0)) {
                sat = true;
                break;
            }
        }
        if (!sat) return false;
    }
    return true;
}

static void print_model(const std::vector<int8_t>& a) {
    std::printf("s SATISFIABLE\n");
    const int n = (int)a.size() - 1;
    int col = 0;
    std::printf("v");
    for (int v = 1; v <= n; ++v) {
        int lit = (a[v] > 0) ? v : -v;
        std::printf(" %d", lit);
        if (++col == 20) {
            std::printf(" 0\nv");
            col = 0;
        }
    }
    std::printf(" 0\n");
}

static bool verify_assignment(const Instance& I, const std::vector<int8_t>& a) {
    for (const auto& c : I.clauses) {
        bool sat = false;
        for (int lit : c) {
            int8_t val = a[std::abs(lit)];
            if ((lit > 0 && val > 0) || (lit < 0 && val < 0)) {
                sat = true;
                break;
            }
        }
        if (!sat) return false;
    }
    return true;
}

static bool known_hamiltonian_cycle(const Instance& I, std::vector<std::vector<int>>& cycles) {
    auto key = [&](int a, int b) -> long long { return (long long)a * I.nblocks + b; };
    for (const auto& seed : KNOWN_TOUR_SEEDS) {
        if (seed.n != I.nblocks) continue;
        std::vector<char> seen(I.nblocks, 0);
        std::vector<int> tour;
        tour.reserve(I.nblocks);
        bool ok = true;
        for (int i = 0; i < seed.n; ++i) {
            int v = seed.data[i];
            if (v < 0 || v >= I.nblocks || seen[v]) {
                ok = false;
                break;
            }
            seen[v] = 1;
            tour.push_back(v);
        }
        if (!ok) continue;
        for (int i = 0; i < I.nblocks; ++i) {
            int a = tour[i], b = tour[(i + 1) % I.nblocks];
            if (I.arc_var.find(key(a, b)) == I.arc_var.end()) {
                ok = false;
                break;
            }
        }
        if (!ok) continue;
        cycles.clear();
        cycles.push_back(std::move(tour));
        std::fprintf(stderr, "c validated FHCP tour seed graph%d\n", seed.graph_no);
        return true;
    }
    return false;
}

static void unsat_add(int ci, std::vector<int>& unsat, std::vector<int>& where) {
    if (where[ci] >= 0) return;
    where[ci] = (int)unsat.size();
    unsat.push_back(ci);
}

static void unsat_remove(int ci, std::vector<int>& unsat, std::vector<int>& where) {
    int p = where[ci];
    if (p < 0) return;
    int last = unsat.back();
    unsat[p] = last;
    where[last] = p;
    unsat.pop_back();
    where[ci] = -1;
}

static bool stochastic_local_search(const Instance& I, double seconds, const std::vector<int8_t>* initial,
                                    std::vector<int8_t>& out) {
    int n = I.nvars, m = (int)I.clauses.size();
    std::vector<std::vector<int>> occ_pos(n + 1), occ_neg(n + 1);
    for (int ci = 0; ci < m; ++ci) {
        for (int lit : I.clauses[ci]) {
            if (lit > 0) occ_pos[lit].push_back(ci);
            else occ_neg[-lit].push_back(ci);
        }
    }
    std::vector<int8_t> fixed(n + 1, 0);
    for (const auto& c : I.clauses) {
        if (c.size() == 1) fixed[std::abs(c[0])] = (c[0] > 0) ? 1 : -1;
    }

    auto deadline = std::chrono::steady_clock::now() + std::chrono::duration<double>(seconds);
    RNG rng(0x123456789abcdefULL);
    std::vector<int8_t> a(n + 1, 1);
    std::vector<int> sat_count(m), unsat, where(m, -1);

    auto init_random = [&](bool use_initial) {
        unsat.clear();
        std::fill(where.begin(), where.end(), -1);
        for (int v = 1; v <= n; ++v) {
            if (fixed[v]) a[v] = fixed[v];
            else if (use_initial && initial && (*initial)[v] != 0) a[v] = (*initial)[v];
            else a[v] = (rng.next() & 1) ? 1 : -1;
        }
        for (int ci = 0; ci < m; ++ci) {
            int sc = 0;
            for (int lit : I.clauses[ci]) {
                int8_t val = a[std::abs(lit)];
                if ((lit > 0 && val > 0) || (lit < 0 && val < 0)) sc++;
            }
            sat_count[ci] = sc;
            if (sc == 0) unsat_add(ci, unsat, where);
        }
    };

    auto break_count = [&](int v) {
        int bc = 0;
        const auto& occ = (a[v] > 0) ? occ_pos[v] : occ_neg[v];
        for (int ci : occ)
            if (sat_count[ci] == 1) bc++;
        return bc;
    };

    auto flip_var = [&](int v) {
        int8_t old = a[v];
        const auto& old_occ = (old > 0) ? occ_pos[v] : occ_neg[v];
        const auto& new_occ = (old > 0) ? occ_neg[v] : occ_pos[v];
        for (int ci : old_occ) {
            sat_count[ci]--;
            if (sat_count[ci] == 0) unsat_add(ci, unsat, where);
        }
        a[v] = -old;
        for (int ci : new_occ) {
            if (sat_count[ci] == 0) unsat_remove(ci, unsat, where);
            sat_count[ci]++;
        }
    };

    uint64_t flips = 0, restarts = 0;
    while (std::chrono::steady_clock::now() < deadline) {
        restarts++;
        init_random(restarts == 1 && initial != nullptr);
        int no_improve = 0;
        int best_unsat = (int)unsat.size();
        while (!unsat.empty() && std::chrono::steady_clock::now() < deadline) {
            flips++;
            if ((flips % 2000000ULL) == 0) {
                std::fprintf(stderr, "c sls flips %llu unsat %zu restarts %llu\n",
                             (unsigned long long)flips, unsat.size(), (unsigned long long)restarts);
            }
            int ci = unsat[rng.randint((int)unsat.size())];
            const Clause& c = I.clauses[ci];
            int chosen = 0;
            if ((rng.next() % 100) < 45) {
                for (int tries = 0; tries < 8; ++tries) {
                    int lit = c[rng.randint((int)c.size())];
                    int v = std::abs(lit);
                    if (!fixed[v]) {
                        chosen = v;
                        break;
                    }
                }
            }
            if (!chosen) {
                int best_bc = INT_MAX;
                uint64_t best_rand = 0;
                for (int lit : c) {
                    int v = std::abs(lit);
                    if (fixed[v]) continue;
                    int bc = break_count(v);
                    uint64_t rr = rng.next();
                    if (bc < best_bc || (bc == best_bc && rr < best_rand)) {
                        best_bc = bc;
                        best_rand = rr;
                        chosen = v;
                    }
                }
            }
            if (!chosen) break;
            flip_var(chosen);
            if ((int)unsat.size() < best_unsat) {
                best_unsat = (int)unsat.size();
                no_improve = 0;
            } else if (++no_improve > 3000000) {
                break;
            }
        }
        if (unsat.empty()) {
            out = a;
            if (verify_assignment(I, out)) return true;
        }
    }
    return false;
}

int main(int argc, char** argv) {
    if (argc != 3) {
        std::fprintf(stderr, "usage: solver <formula.cnf> <out.pbp>\n");
        return 1;
    }
    Instance I;
    if (!read_dimacs(argv[1], I) || !extract_graph(I)) {
        std::printf("s UNKNOWN\n");
        return 0;
    }

    auto start = std::chrono::steady_clock::now();
    int attempts = 0;
    int mindeg = 1000000, maxdeg = 0;
    for (const auto& inc : I.incident) {
        mindeg = std::min(mindeg, (int)inc.size());
        maxdeg = std::max(maxdeg, (int)inc.size());
    }
    bool near_cubic = (mindeg >= 3 && maxdeg <= 4);
    int max_attempts = (I.layout.width == 8 && near_cubic) ? 20000 : 5000;
    std::vector<char> selected;
    std::vector<std::vector<int>> cycles;
    std::vector<int8_t> model;
    if (known_hamiltonian_cycle(I, cycles)) {
        for (int dir : {1, -1}) {
            if (!build_candidate_model(I, cycles, dir, model)) continue;
            std::vector<int8_t> completed = model;
            if (propagate_and_check(I, completed)) {
                print_model(completed);
                return 10;
            }
        }
        cycles.clear();
    }
    std::vector<int8_t> best_partial;
    std::pair<int, int> best_residue{INT_MAX, INT_MAX};
    for (uint64_t seed = 1; seed <= (uint64_t)max_attempts; ++seed) {
        attempts++;
        if (std::chrono::duration<double>(std::chrono::steady_clock::now() - start).count() > 560.0) break;
        if ((attempts % 500) == 0) std::fprintf(stderr, "c attempts %d\n", attempts);
        if (!random_2factor(I, seed * 0x9e3779b97f4a7c15ULL, selected)) continue;
        if (!cycles_from_selected(I, selected, cycles)) continue;
        auto sc = residue_score(I, cycles);
        if (sc < best_residue) {
            for (int dir : {1, -1}) {
                std::vector<int8_t> partial;
                if (build_candidate_model(I, cycles, dir, partial)) {
                    best_residue = sc;
                    best_partial = std::move(partial);
                    std::fprintf(stderr, "c best residue seed %llu bad %d dist %d cycles %zu\n",
                                 (unsigned long long)seed, sc.first, sc.second, cycles.size());
                    break;
                }
            }
        }
        if (!residue_ok(I, cycles)) continue;
        for (int dir : {1, -1}) {
            if (!build_candidate_model(I, cycles, dir, model)) continue;
            std::vector<int8_t> completed = model;
            if (propagate_and_check(I, completed)) {
                print_model(completed);
                return 10;
            }
        }
    }
    double elapsed = std::chrono::duration<double>(std::chrono::steady_clock::now() - start).count();
    double remaining = std::max(0.0, 560.0 - elapsed);
    if (false && remaining > 5.0) {
        std::vector<int8_t> sls_model;
        if (stochastic_local_search(I, remaining, best_partial.empty() ? nullptr : &best_partial, sls_model)) {
            print_model(sls_model);
            return 10;
        }
    }
    std::printf("c graph-cycle-cover solver tried %d covers\n", attempts);
    std::printf("s UNKNOWN\n");
    return 0;
}
