#include <algorithm>
#include <array>
#include <chrono>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <fstream>
#include <functional>
#include <iostream>
#include <numeric>
#include <random>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>

using Clock = std::chrono::steady_clock;

struct Formula {
    int nvars = 0;
    std::vector<int> lits;
    std::vector<int> off;
};

struct Instance {
    int D = 0, P = 0, K = 0, L = 0, keyVars = 0;
    std::vector<std::vector<int>> locks;
    std::vector<int> lockVars;
    std::unordered_map<int, std::pair<int,int>> blockImp;
};

static Formula parse_cnf(const std::string& path) {
    std::ifstream in(path);
    Formula f;
    std::string tok;
    f.off.push_back(0);
    while (in >> tok) {
        if (tok == "c") {
            std::string rest;
            std::getline(in, rest);
        } else if (tok == "p") {
            std::string fmt;
            int m;
            in >> fmt >> f.nvars >> m;
            f.off.reserve((size_t)m + 1);
        } else {
            int lit = std::stoi(tok);
            if (lit == 0) {
                f.off.push_back((int)f.lits.size());
            } else {
                f.lits.push_back(lit);
                while (in >> lit) {
                    if (lit == 0) break;
                    f.lits.push_back(lit);
                }
                f.off.push_back((int)f.lits.size());
            }
        }
    }
    return f;
}

static int igcd(int a, int b) {
    if (a < 0) a = -a;
    if (b < 0) b = -b;
    while (b) { int t = a % b; a = b; b = t; }
    return a;
}

static bool detect(const Formula& f, Instance& ins) {
    int m = (int)f.off.size() - 1;
    if (m <= 0) return false;
    ins.D = f.off[1] - f.off[0];
    if (ins.D < 2 || ins.D > 8) return false;
    int T = 0;
    while (T < m) {
        int a = f.off[T], b = f.off[T+1];
        if (b - a != ins.D) break;
        bool ok = true;
        for (int i = 0; i < ins.D; ++i) {
            if (f.lits[a+i] <= 0 || (i && f.lits[a+i] != f.lits[a] + i)) ok = false;
        }
        if (!ok) break;
        ++T;
    }
    if (T == 0) return false;
    ins.keyVars = T * ins.D;
    int idx = T + T * (ins.D * (ins.D - 1) / 2);
    if (idx >= m) return false;

    std::vector<std::pair<int, std::vector<int>>> defs;
    while (idx < m) {
        int a = f.off[idx], b = f.off[idx+1];
        if (b - a != 2) break;
        int keylit = f.lits[a], aux = f.lits[a+1];
        if (keylit >= 0 || -keylit > ins.keyVars || aux <= ins.keyVars) break;
        std::vector<int> arr;
        while (idx < m) {
            a = f.off[idx]; b = f.off[idx+1];
            if (b - a == 2 && f.lits[a] < 0 && -f.lits[a] <= ins.keyVars && f.lits[a+1] == aux) {
                arr.push_back(-f.lits[a]);
                ++idx;
            } else break;
        }
        if (idx >= m) return false;
        a = f.off[idx]; b = f.off[idx+1];
        if (b - a != (int)arr.size() + 1 || f.lits[a] != -aux) return false;
        for (int i = 0; i < (int)arr.size(); ++i) if (f.lits[a+1+i] != arr[i]) return false;
        defs.push_back({aux, arr});
        ++idx;
    }
    if (defs.empty()) return false;
    int g = 0;
    for (auto& pr: defs) {
        auto& a = pr.second;
        for (int i = 1; i < (int)a.size(); ++i) g = igcd(g, a[i] - a[i-1]);
    }
    if (g <= 0 || g % ins.D) return false;
    ins.P = g / ins.D;
    if (ins.P <= 0 || T % ins.P) return false;
    ins.K = T / ins.P;
    int lpd = ins.P * ins.D;
    if ((int)defs.size() % lpd) return false;
    ins.L = (int)defs.size() / lpd;
    ins.locks.assign(ins.L, {});
    ins.lockVars.resize(defs.size());
    for (int l = 0; l < ins.L; ++l) {
        std::vector<int> keys;
        for (int v: defs[l*lpd].second) keys.push_back((v - 1) / (ins.P * ins.D));
        std::sort(keys.begin(), keys.end());
        keys.erase(std::unique(keys.begin(), keys.end()), keys.end());
        ins.locks[l] = keys;
        for (int t = 0; t < lpd; ++t) ins.lockVars[l*lpd+t] = defs[l*lpd+t].first;
    }
    for (; idx < m; ++idx) {
        int a = f.off[idx], b = f.off[idx+1];
        if (b - a == 2 && f.lits[a] < 0) {
            int x = -f.lits[a], y = f.lits[a+1];
            if (x > ins.keyVars) {
                auto& p = ins.blockImp[x];
                if (y > 0 && y <= ins.keyVars) p.first = y;
                else if (y < 0 && -y > ins.keyVars) p.second = -y;
            }
        }
    }
    return ins.D >= 2 && ins.P >= 2 && ins.K > 0 && ins.L > 0;
}

static std::vector<std::array<uint8_t, 8>> make_codes(int D, int P) {
    std::vector<std::array<uint8_t, 8>> out;
    std::array<uint8_t, 8> cur{};
    auto rec = [&](auto&& self, int p) -> void {
        if (p == P) { out.push_back(cur); return; }
        for (int d = 0; d < D; ++d) {
            if (p == 0 || std::abs((int)cur[p-1] - d) <= 1) {
                cur[p] = (uint8_t)d;
                self(self, p+1);
            }
        }
    };
    if (P <= 8) rec(rec, 0);
    return out;
}

struct Searcher {
    const Instance& ins;
    std::vector<std::array<uint8_t, 8>> codes;
    std::vector<std::vector<int>> unauth, contains;
    std::vector<std::vector<char>> lockHas;
    std::mt19937 rng;

    Searcher(const Instance& i): ins(i), codes(make_codes(i.D, i.P)), contains(i.K),
                                 lockHas(i.L, std::vector<char>(i.K, 0)) {
        std::vector<char> in(i.K);
        for (int l = 0; l < ins.L; ++l) {
            std::fill(in.begin(), in.end(), 0);
            for (int k: ins.locks[l]) { in[k] = 1; lockHas[l][k] = 1; contains[k].push_back(l); }
            unauth.push_back({});
            for (int k = 0; k < ins.K; ++k) if (!in[k]) unauth.back().push_back(k);
        }
        rng.seed(1234567u + i.K * 131u + i.L);
    }

    int eval(const std::vector<int>& asg, int* soft = nullptr) const {
        int hard = 0, s = 0;
        std::array<uint8_t, 8> masks{};
        for (int l = 0; l < ins.L; ++l) {
            masks.fill(0);
            for (int k: ins.locks[l]) {
                const auto& c = codes[asg[k]];
                for (int p = 0; p < ins.P; ++p) masks[p] |= (uint8_t)(1u << c[p]);
            }
            for (int p = 0; p < ins.P; ++p) {
                int pc = __builtin_popcount((unsigned)masks[p]);
                s += pc * pc;
            }
            for (int k: unauth[l]) {
                const auto& c = codes[asg[k]];
                bool acc = true;
                for (int p = 0; p < ins.P; ++p) if (((masks[p] >> c[p]) & 1u) == 0) { acc = false; break; }
                hard += acc;
            }
        }
        if (soft) *soft = s;
        return hard;
    }

    int eval_cuts(const std::vector<std::array<uint8_t,8>>& x,
                  std::vector<std::pair<int,int>>* bad = nullptr) const {
        int hard = 0;
        if (bad) bad->clear();
        std::array<uint8_t, 8> masks{};
        for (int l = 0; l < ins.L; ++l) {
            masks.fill(0);
            for (int k: ins.locks[l]) {
                for (int p = 0; p < ins.P; ++p) masks[p] |= (uint8_t)(1u << x[k][p]);
            }
            for (int k = 0; k < ins.K; ++k) {
                if (lockHas[l][k]) continue;
                bool acc = true;
                for (int p = 0; p < ins.P; ++p) if (((masks[p] >> x[k][p]) & 1u) == 0) { acc = false; break; }
                if (acc) {
                    ++hard;
                    if (bad) bad->push_back({l, k});
                }
            }
        }
        return hard;
    }

    struct IncState {
        const Searcher& s;
        std::vector<std::array<uint8_t,8>> x;
        std::vector<uint8_t> cnt, masks, viol;
        std::vector<int> keyViol, weight;
        int total = 0;
        long long wscore = 0;

        IncState(const Searcher& ss, const std::vector<std::array<uint8_t,8>>& init)
            : s(ss), x(init),
              cnt((size_t)ss.ins.L * ss.ins.P * ss.ins.D, 0),
              masks((size_t)ss.ins.L * ss.ins.P, 0),
              viol((size_t)ss.ins.L * ss.ins.K, 0),
              keyViol(ss.ins.K, 0),
              weight((size_t)ss.ins.L * ss.ins.K, 1) {
            for (int l = 0; l < s.ins.L; ++l) {
                for (int k: s.ins.locks[l]) {
                    for (int p = 0; p < s.ins.P; ++p) cnt[cidx(l, p, x[k][p])]++;
                }
                for (int p = 0; p < s.ins.P; ++p) remask(l, p);
            }
            for (int l = 0; l < s.ins.L; ++l) {
                for (int k: s.unauth[l]) {
                    bool v = opens(l, k);
                    if (v) {
                        viol[vidx(l, k)] = 1;
                        keyViol[k]++;
                        total++;
                        wscore += weight[vidx(l, k)];
                    }
                }
            }
        }

        int cidx(int l, int p, int d) const { return (l * s.ins.P + p) * s.ins.D + d; }
        int midx(int l, int p) const { return l * s.ins.P + p; }
        int vidx(int l, int k) const { return l * s.ins.K + k; }

        void remask(int l, int p) {
            uint8_t m = 0;
            for (int d = 0; d < s.ins.D; ++d) if (cnt[cidx(l, p, d)]) m |= (uint8_t)(1u << d);
            masks[midx(l, p)] = m;
        }

        bool opens(int l, int k) const {
            for (int p = 0; p < s.ins.P; ++p) {
                if (((masks[midx(l, p)] >> x[k][p]) & 1u) == 0) return false;
            }
            return true;
        }

        bool can_move(int k, int p, int d) const {
            if (d == x[k][p]) return false;
            if (p > 0 && std::abs((int)x[k][p-1] - d) > 1) return false;
            if (p + 1 < s.ins.P && std::abs((int)x[k][p+1] - d) > 1) return false;
            return true;
        }

        void set_pair(int l, int k, bool nv) {
            int id = vidx(l, k);
            bool ov = viol[id] != 0;
            if (ov == nv) return;
            viol[id] = nv ? 1 : 0;
            int delta = nv ? 1 : -1;
            total += delta;
            keyViol[k] += delta;
            wscore += (long long)delta * weight[id];
        }

        void refresh_pair(int l, int k) {
            if (s.lockHas[l][k]) return;
            set_pair(l, k, opens(l, k));
        }

        int move_cell(int k, int p, int d) {
            int old = x[k][p];
            if (old == d) return 0;
            int before = total;
            x[k][p] = (uint8_t)d;
            for (int l = 0; l < s.ins.L; ++l) {
                if (!s.lockHas[l][k]) refresh_pair(l, k);
            }
            for (int l: s.contains[k]) {
                cnt[cidx(l, p, old)]--;
                cnt[cidx(l, p, d)]++;
                remask(l, p);
                for (int j: s.unauth[l]) refresh_pair(l, j);
            }
            return total - before;
        }

        void collect_bad(std::vector<std::pair<int,int>>& bad) const {
            bad.clear();
            for (int l = 0; l < s.ins.L; ++l) {
                for (int k: s.unauth[l]) if (viol[vidx(l, k)]) bad.push_back({l, k});
            }
        }

        void bump_weights() {
            for (int l = 0; l < s.ins.L; ++l) {
                for (int k: s.unauth[l]) {
                    int id = vidx(l, k);
                    if (viol[id]) {
                        weight[id]++;
                        wscore++;
                    }
                }
            }
        }
    };

    int column_coverage(const std::vector<uint8_t>& colors,
                        const std::vector<std::pair<int,int>>& pairs,
                        const std::vector<uint8_t>& active) const {
        std::vector<uint8_t> lm(ins.L, 0);
        for (int l = 0; l < ins.L; ++l) {
            uint8_t m = 0;
            for (int k: ins.locks[l]) m |= (uint8_t)(1u << colors[k]);
            lm[l] = m;
        }
        int cov = 0;
        for (int i = 0; i < (int)pairs.size(); ++i) {
            if (!active[i]) continue;
            int l = pairs[i].first, k = pairs[i].second;
            if (((lm[l] >> colors[k]) & 1u) == 0) cov++;
        }
        return cov;
    }

    std::vector<std::array<uint8_t,8>> greedy_cover_init(int iters_per_col) {
        std::vector<std::pair<int,int>> pairs;
        for (int l = 0; l < ins.L; ++l) {
            for (int k: unauth[l]) pairs.push_back({l, k});
        }
        std::vector<uint8_t> active(pairs.size(), 1), colors(ins.K), bestColors(ins.K), prev(ins.K, 255);
        std::vector<std::array<uint8_t,8>> x(ins.K);
        int uncovered = (int)pairs.size();
        for (int p = 0; p < ins.P; ++p) {
            for (int k = 0; k < ins.K; ++k) {
                int lo = 0, hi = ins.D - 1;
                if (prev[k] != 255) {
                    lo = std::max(0, (int)prev[k] - 1);
                    hi = std::min(ins.D - 1, (int)prev[k] + 1);
                }
                colors[k] = (uint8_t)(lo + (int)(rng() % (uint32_t)(hi - lo + 1)));
            }
            int cur = column_coverage(colors, pairs, active);
            int best = cur, stagnant = 0;
            bestColors = colors;
            int iters = std::max(iters_per_col, ins.K * 12);
            for (int it = 0; it < iters; ++it) {
                int k = rng() % ins.K;
                int lo = 0, hi = ins.D - 1;
                if (prev[k] != 255) {
                    lo = std::max(0, (int)prev[k] - 1);
                    hi = std::min(ins.D - 1, (int)prev[k] + 1);
                }
                uint8_t old = colors[k], bestD = old;
                int bestLocal = cur;
                for (int d = lo; d <= hi; ++d) {
                    if (d == old) continue;
                    colors[k] = (uint8_t)d;
                    int c = column_coverage(colors, pairs, active);
                    if (c > bestLocal || (c == bestLocal && (rng() & 1023u) == 0)) {
                        bestLocal = c;
                        bestD = (uint8_t)d;
                    }
                }
                colors[k] = bestD;
                if (bestLocal >= cur) cur = bestLocal;
                else colors[k] = old;
                if (cur > best) {
                    best = cur;
                    bestColors = colors;
                    stagnant = 0;
                } else if (++stagnant > ins.K * 25) {
                    for (int r = 0; r < std::max(1, ins.K / 12); ++r) {
                        int kk = rng() % ins.K;
                        int lo2 = 0, hi2 = ins.D - 1;
                        if (prev[kk] != 255) {
                            lo2 = std::max(0, (int)prev[kk] - 1);
                            hi2 = std::min(ins.D - 1, (int)prev[kk] + 1);
                        }
                        colors[kk] = (uint8_t)(lo2 + (int)(rng() % (uint32_t)(hi2 - lo2 + 1)));
                    }
                    cur = column_coverage(colors, pairs, active);
                    stagnant = 0;
                }
            }
            colors = bestColors;
            std::vector<uint8_t> lm(ins.L, 0);
            for (int l = 0; l < ins.L; ++l) {
                uint8_t m = 0;
                for (int k: ins.locks[l]) m |= (uint8_t)(1u << colors[k]);
                lm[l] = m;
            }
            int left = 0;
            for (int i = 0; i < (int)pairs.size(); ++i) {
                if (!active[i]) continue;
                int l = pairs[i].first, k = pairs[i].second;
                if (((lm[l] >> colors[k]) & 1u) == 0) active[i] = 0;
                else left++;
            }
            uncovered = left;
            for (int k = 0; k < ins.K; ++k) {
                x[k][p] = colors[k];
                prev[k] = colors[k];
            }
            std::cout << "c greedy uncovered after position " << p << ": " << uncovered << "\n" << std::flush;
        }
        return x;
    }

    bool repair_incremental(const std::vector<std::array<uint8_t,8>>& init,
                            std::vector<std::array<uint8_t,8>>& cuts,
                            Clock::time_point deadline) {
        IncState st(*this, init);
        int best = st.total;
        std::vector<std::array<uint8_t,8>> bestX = st.x;
        std::cout << "c incremental start violations " << st.total << "\n" << std::flush;
        if (st.total == 0) { cuts = st.x; return true; }

        struct Move { int k, p, d; };
        std::vector<std::pair<int,int>> bad;
        std::vector<Move> moves;
        std::vector<uint8_t> used((size_t)ins.K * ins.P * ins.D, 0);
        std::uniform_real_distribution<double> unit(0.0, 1.0);
        int stagnant = 0;
        long long steps = 0;
        auto add_move = [&](int k, int p, int d) {
            if (k < 0 || k >= ins.K || p < 0 || p >= ins.P || d < 0 || d >= ins.D) return;
            if (!st.can_move(k, p, d)) return;
            int id = (k * ins.P + p) * ins.D + d;
            if (used[id]) return;
            used[id] = 1;
            moves.push_back({k, p, d});
        };
        auto add_to = [&](std::vector<Move>& out, std::vector<uint8_t>& seen,
                          int k, int p, int d) {
            if (k < 0 || k >= ins.K || p < 0 || p >= ins.P || d < 0 || d >= ins.D) return;
            if (!st.can_move(k, p, d)) return;
            int id = (k * ins.P + p) * ins.D + d;
            if (seen[id]) return;
            seen[id] = 1;
            out.push_back({k, p, d});
        };
        auto gen_low_moves = [&](std::vector<Move>& out, std::vector<uint8_t>& seen) {
            std::vector<std::pair<int,int>> curBad;
            st.collect_bad(curBad);
            for (auto pr: curBad) {
                int l = pr.first, blocked = pr.second;
                for (int p = 0; p < ins.P; ++p) {
                    uint8_t m = st.masks[st.midx(l, p)];
                    for (int d = 0; d < ins.D; ++d) {
                        if (((m >> d) & 1u) == 0) add_to(out, seen, blocked, p, d);
                    }
                    int bd = st.x[blocked][p];
                    if (st.cnt[st.cidx(l, p, bd)] == 1) {
                        for (int a: ins.locks[l]) if (st.x[a][p] == bd) {
                            for (int d = 0; d < ins.D; ++d) add_to(out, seen, a, p, d);
                        }
                    }
                    if (ins.K <= 60) {
                        for (int a: ins.locks[l]) {
                            for (int d = 0; d < ins.D; ++d) add_to(out, seen, a, p, d);
                        }
                    }
                }
            }
        };
        std::function<bool(int,int&)> low_dfs = [&](int depth, int& nodes) -> bool {
            if (st.total == 0) { cuts = st.x; return true; }
            int nodeLimit = ins.K <= 60 ? 180000 : 90000;
            if (depth == 0 || ++nodes > nodeLimit || Clock::now() >= deadline) return false;
            std::vector<Move> localMoves;
            std::vector<uint8_t> localSeen((size_t)ins.K * ins.P * ins.D, 0);
            gen_low_moves(localMoves, localSeen);
            if (localMoves.empty()) return false;
            struct LocalScore { int dt; long long dw; Move mv; };
            std::vector<LocalScore> localScores;
            localScores.reserve(localMoves.size());
            int oldTotal = st.total;
            long long oldW = st.wscore;
            for (const Move& mv: localMoves) {
                int old = st.x[mv.k][mv.p];
                st.move_cell(mv.k, mv.p, mv.d);
                localScores.push_back({st.total - oldTotal, st.wscore - oldW, mv});
                st.move_cell(mv.k, mv.p, old);
            }
            std::sort(localScores.begin(), localScores.end(), [](const LocalScore& a, const LocalScore& b) {
                if (a.dt != b.dt) return a.dt < b.dt;
                return a.dw < b.dw;
            });
            int lim;
            if (ins.K <= 60) lim = (depth >= 4 ? 180 : (depth == 3 ? 110 : 70));
            else lim = (depth >= 4 ? 44 : 34);
            lim = std::min<int>((int)localScores.size(), lim);
            for (int i = 0; i < lim; ++i) {
                Move mv = localScores[i].mv;
                int old = st.x[mv.k][mv.p];
                st.move_cell(mv.k, mv.p, mv.d);
                if (low_dfs(depth - 1, nodes)) return true;
                st.move_cell(mv.k, mv.p, old);
            }
            return false;
        };
        auto apply_code = [&](int k, const std::array<uint8_t,8>& code) {
            for (int p = 0; p < ins.P; ++p) {
                if (st.x[k][p] != code[p]) st.move_cell(k, p, code[p]);
            }
        };
        auto try_code_moves = [&]() -> bool {
            if (ins.K > 60 || codes.empty()) return false;
            std::vector<std::pair<int,int>> curBad;
            st.collect_bad(curBad);
            if (curBad.empty()) { cuts = st.x; return true; }
            std::vector<uint8_t> take(ins.K, 0);
            std::vector<int> keys;
            auto mark = [&](int k) {
                if (!take[k]) {
                    take[k] = 1;
                    keys.push_back(k);
                }
            };
            for (auto pr: curBad) {
                mark(pr.second);
                for (int a: ins.locks[pr.first]) mark(a);
            }
            struct CodeScore { int dt; long long dw; int k; int ci; };
            CodeScore bestCode{0, 0, -1, -1};
            int oldTotal = st.total;
            long long oldW = st.wscore;
            for (int k: keys) {
                auto old = st.x[k];
                for (int ci = 0; ci < (int)codes.size(); ++ci) {
                    if (codes[ci] == old) continue;
                    apply_code(k, codes[ci]);
                    int dt = st.total - oldTotal;
                    long long dw = st.wscore - oldW;
                    if (bestCode.k < 0 || dt < bestCode.dt || (dt == bestCode.dt && dw < bestCode.dw)) {
                        bestCode = {dt, dw, k, ci};
                    }
                    apply_code(k, old);
                }
                if (Clock::now() >= deadline) break;
            }
            if (bestCode.k >= 0 && (bestCode.dt < 0 || (bestCode.dt == 0 && bestCode.dw < 0))) {
                apply_code(bestCode.k, codes[bestCode.ci]);
                return st.total == 0;
            }
            return false;
        };
        auto try_column_reopt = [&]() -> bool {
            if (ins.K > 80) return false;
            struct ColResult {
                int total;
                long long score;
            };
            std::vector<uint8_t> color(ins.K), bestColor(ins.K), curColor(ins.K), lo(ins.K), hi(ins.K);
            std::vector<std::pair<int,int>> need;
            std::vector<uint8_t> lm(ins.L);
            auto eval_col = [&](int p, const std::vector<uint8_t>& col) -> ColResult {
                for (int l = 0; l < ins.L; ++l) {
                    uint8_t m = 0;
                    for (int k: ins.locks[l]) m |= (uint8_t)(1u << col[k]);
                    lm[l] = m;
                }
                ColResult r{0, 0};
                for (auto pr: need) {
                    int l = pr.first, k = pr.second;
                    if ((lm[l] >> col[k]) & 1u) {
                        r.total++;
                        r.score += st.weight[st.vidx(l, k)];
                    }
                }
                return r;
            };
            bool changed = false;
            for (int p = 0; p < ins.P && Clock::now() < deadline; ++p) {
                for (int k = 0; k < ins.K; ++k) {
                    int a = 0, b = ins.D - 1;
                    if (p > 0) {
                        a = std::max(a, (int)st.x[k][p-1] - 1);
                        b = std::min(b, (int)st.x[k][p-1] + 1);
                    }
                    if (p + 1 < ins.P) {
                        a = std::max(a, (int)st.x[k][p+1] - 1);
                        b = std::min(b, (int)st.x[k][p+1] + 1);
                    }
                    lo[k] = (uint8_t)a;
                    hi[k] = (uint8_t)b;
                    color[k] = st.x[k][p];
                }
                need.clear();
                for (int l = 0; l < ins.L; ++l) {
                    for (int k: unauth[l]) {
                        bool blockedElse = false;
                        for (int q = 0; q < ins.P; ++q) {
                            if (q == p) continue;
                            if (((st.masks[st.midx(l, q)] >> st.x[k][q]) & 1u) == 0) {
                                blockedElse = true;
                                break;
                            }
                        }
                        if (!blockedElse) need.push_back({l, k});
                    }
                }
                if (need.empty()) continue;
                bestColor = color;
                ColResult best = eval_col(p, bestColor);
                curColor = color;
                ColResult cur = best;
                int restarts = 4;
                int iters = std::max(900, ins.K * 80);
                for (int r = 0; r < restarts && Clock::now() < deadline; ++r) {
                    if (r > 0) {
                        for (int k = 0; k < ins.K; ++k) {
                            curColor[k] = (uint8_t)(lo[k] + (int)(rng() % (uint32_t)(hi[k] - lo[k] + 1)));
                        }
                        cur = eval_col(p, curColor);
                    }
                    int stagnantCol = 0;
                    for (int it = 0; it < iters && Clock::now() < deadline; ++it) {
                        if (cur.total < best.total || (cur.total == best.total && cur.score < best.score)) {
                            best = cur;
                            bestColor = curColor;
                            stagnantCol = 0;
                        } else if (++stagnantCol > ins.K * 20) {
                            break;
                        }
                        int k = rng() % ins.K;
                        uint8_t old = curColor[k], bd = old;
                        ColResult br = cur;
                        for (int d = lo[k]; d <= hi[k]; ++d) {
                            if (d == old) continue;
                            curColor[k] = (uint8_t)d;
                            ColResult nr = eval_col(p, curColor);
                            if (nr.total < br.total || (nr.total == br.total && nr.score < br.score) ||
                                (nr.total == br.total && nr.score == br.score && (rng() & 2047u) == 0)) {
                                br = nr;
                                bd = (uint8_t)d;
                            }
                        }
                        curColor[k] = bd;
                        cur = br;
                        if ((rng() % 1000) < 7) {
                            int kk = rng() % ins.K;
                            curColor[kk] = (uint8_t)(lo[kk] + (int)(rng() % (uint32_t)(hi[kk] - lo[kk] + 1)));
                            cur = eval_col(p, curColor);
                        }
                    }
                }
                if (best.total < st.total || (best.total == st.total && best.score < st.wscore)) {
                    for (int k = 0; k < ins.K; ++k) {
                        if (st.x[k][p] != bestColor[k]) st.move_cell(k, p, bestColor[k]);
                    }
                    changed = true;
                    if (st.total == 0) { cuts = st.x; return true; }
                }
            }
            return changed && st.total == 0;
        };

        while (Clock::now() < deadline) {
            if (st.total < best) {
                best = st.total;
                bestX = st.x;
                stagnant = 0;
                std::cout << "c best incremental violations " << best
                          << " step " << steps << "\n" << std::flush;
                if (best == 0) { cuts = st.x; return true; }
                if (best <= 80 && try_column_reopt()) return true;
                if (best <= 16 && try_code_moves()) return true;
                int dfsTrigger = ins.K > 100 ? 0 : 2;
                if (best <= dfsTrigger) {
                    int nodes = 0;
                    if (low_dfs(ins.K <= 60 ? 5 : 4, nodes)) return true;
                }
            }
            if (ins.K <= 80 && st.total <= 80 && (steps % 1536) == 0) {
                if (try_column_reopt()) return true;
            }
            if (ins.K <= 60 && st.total <= 16 && (steps % 512) == 0) {
                if (try_code_moves()) return true;
            }
            st.collect_bad(bad);
            if (bad.empty()) { cuts = st.x; return true; }
            std::fill(used.begin(), used.end(), 0);
            moves.clear();

            int samples = std::min<int>((int)bad.size(), ins.K > 100 ? 18 : 28);
            for (int t = 0; t < samples; ++t) {
                auto pr = bad[rng() % bad.size()];
                int l = pr.first, blocked = pr.second;
                for (int p = 0; p < ins.P; ++p) {
                    uint8_t m = st.masks[st.midx(l, p)];
                    for (int d = 0; d < ins.D; ++d) {
                        if (((m >> d) & 1u) == 0) add_move(blocked, p, d);
                    }
                    int bd = st.x[blocked][p];
                    if (st.cnt[st.cidx(l, p, bd)] == 1) {
                        for (int a: ins.locks[l]) if (st.x[a][p] == bd) {
                            for (int d = 0; d < ins.D; ++d) add_move(a, p, d);
                        }
                    }
                    if ((rng() & 3u) == 0) {
                        int a = ins.locks[l][rng() % ins.locks[l].size()];
                        for (int d = 0; d < ins.D; ++d) add_move(a, p, d);
                    }
                }
            }
            for (int r = 0; r < (ins.K > 100 ? 80 : 40); ++r) {
                int k;
                if (!bad.empty() && (rng() % 100) < 70) {
                    auto pr = bad[rng() % bad.size()];
                    if ((rng() % 100) < 45) k = pr.second;
                    else k = ins.locks[pr.first][rng() % ins.locks[pr.first].size()];
                } else {
                    k = rng() % ins.K;
                }
                int p = rng() % ins.P;
                for (int d = 0; d < ins.D; ++d) add_move(k, p, d);
            }
            if (moves.empty()) {
                st.bump_weights();
                continue;
            }

            struct Scored { long long dw; int dt; Move mv; };
            std::vector<Scored> scored;
            scored.reserve(moves.size());
            int oldTotal = st.total;
            long long oldScore = st.wscore;
            for (const Move& mv: moves) {
                int old = st.x[mv.k][mv.p];
                st.move_cell(mv.k, mv.p, mv.d);
                scored.push_back({st.wscore - oldScore, st.total - oldTotal, mv});
                st.move_cell(mv.k, mv.p, old);
            }
            std::sort(scored.begin(), scored.end(), [](const Scored& a, const Scored& b) {
                if (a.dw != b.dw) return a.dw < b.dw;
                return a.dt < b.dt;
            });
            Move chosen = scored[0].mv;
            bool accept = scored[0].dw <= 0 || scored[0].dt < 0;
            if (!accept) {
                double temp = std::max(0.15, 4.0 * std::exp(-steps / 25000.0));
                accept = unit(rng) < std::exp(-(double)std::min<long long>(scored[0].dw, 30) / temp);
            }
            if (!accept && (rng() % 100) < 8) {
                int lim = std::min<int>((int)scored.size(), 24);
                chosen = scored[rng() % lim].mv;
                accept = true;
            }
            if (accept) st.move_cell(chosen.k, chosen.p, chosen.d);
            if (++stagnant % 450 == 0) st.bump_weights();
            if (stagnant % 2500 == 0) {
                for (int q = 0; q < std::max(4, ins.K / 16); ++q) {
                    int k = rng() % ins.K, p = rng() % ins.P;
                    std::vector<int> ds;
                    for (int d = 0; d < ins.D; ++d) if (st.can_move(k, p, d)) ds.push_back(d);
                    if (!ds.empty()) st.move_cell(k, p, ds[rng() % ds.size()]);
                }
            }
            steps++;
        }
        if (!bestX.empty()) {
            int v = eval_cuts(bestX, nullptr);
            std::cout << "c final best incremental violations " << v << "\n" << std::flush;
        }
        return false;
    }

    bool solve_greedy_repair(std::vector<std::array<uint8_t,8>>& cuts, double seconds) {
        auto deadline = Clock::now() + std::chrono::milliseconds((int)(seconds * 1000));
        int restart = 0;
        while (Clock::now() < deadline) {
            int iters = (ins.K > 100 ? 2600 : 5200);
            std::cout << "c greedy-repair restart " << restart++ << "\n" << std::flush;
            auto init = greedy_cover_init(iters);
            auto now = Clock::now();
            int chunkSeconds = ins.K > 100 ? 240 : 105;
            auto localDeadline = std::min(deadline, now + std::chrono::seconds(chunkSeconds));
            if (repair_incremental(init, cuts, localDeadline)) return true;
        }
        return false;
    }

    bool solve_cells(std::vector<std::array<uint8_t,8>>& cuts, double seconds) {
        if (codes.empty()) return false;
        auto start = Clock::now();
        auto deadline = start + std::chrono::milliseconds((int)(seconds * 1000));
        std::uniform_int_distribution<int> cd(0, (int)codes.size() - 1);
        std::uniform_real_distribution<double> unit(0.0, 1.0);
        int globalBest = 1e9;
        std::vector<std::array<uint8_t,8>> x(ins.K), bestX;
        auto try_repair = [&](std::vector<std::array<uint8_t,8>> base) -> bool {
            std::vector<std::pair<int,int>> bad;
            if (eval_cuts(base, &bad) == 0) { cuts = base; return true; }
            std::vector<char> use(ins.K, 0);
            for (auto pr: bad) {
                use[pr.second] = 1;
                for (int k: ins.locks[pr.first]) use[k] = 1;
            }
            std::vector<int> keys;
            for (int k = 0; k < ins.K; ++k) if (use[k]) keys.push_back(k);
            struct Move { int k, p, d; };
            std::vector<Move> moves;
            for (int k: keys) for (int p = 0; p < ins.P; ++p) for (int d = 0; d < ins.D; ++d) {
                if (d == base[k][p]) continue;
                if (p > 0 && std::abs((int)base[k][p-1] - d) > 1) continue;
                if (p + 1 < ins.P && std::abs((int)base[k][p+1] - d) > 1) continue;
                moves.push_back({k,p,d});
            }
            int best = eval_cuts(base, nullptr);
            std::vector<std::array<uint8_t,8>> bestLocal = base;
            for (const auto& m1: moves) {
                auto y = base;
                y[m1.k][m1.p] = (uint8_t)m1.d;
                int v1 = eval_cuts(y, nullptr);
                if (v1 == 0) { cuts = y; return true; }
                if (v1 < best) { best = v1; bestLocal = y; }
                for (const auto& m2: moves) {
                    auto z = y;
                    if (m2.k == m1.k && m2.p == m1.p) continue;
                    if (m2.p > 0 && std::abs((int)z[m2.k][m2.p-1] - m2.d) > 1) continue;
                    if (m2.p + 1 < ins.P && std::abs((int)z[m2.k][m2.p+1] - m2.d) > 1) continue;
                    z[m2.k][m2.p] = (uint8_t)m2.d;
                    int v2 = eval_cuts(z, nullptr);
                    if (v2 == 0) { cuts = z; return true; }
                    if (v2 < best) { best = v2; bestLocal = z; }
                }
            }
            if (best < eval_cuts(base, nullptr)) {
                std::cout << "c repair improved to " << best << "\n" << std::flush;
            }
            int nodes = 0;
            auto dfs = [&](auto&& self, std::vector<std::array<uint8_t,8>>& cur, int depth) -> bool {
                std::vector<std::pair<int,int>> badNow;
                int cv = eval_cuts(cur, &badNow);
                if (cv == 0) { cuts = cur; return true; }
                if (depth == 0 || ++nodes > 30000) return false;
                auto pr = badNow[0];
                int l = pr.first, blocked = pr.second;
                std::array<uint8_t,8> masks{};
                std::array<std::array<int,8>,8> cnt{};
                for (int a: ins.locks[l]) {
                    for (int p = 0; p < ins.P; ++p) {
                        masks[p] |= (uint8_t)(1u << cur[a][p]);
                        cnt[p][cur[a][p]]++;
                    }
                }
                std::vector<Move> cand;
                for (int p = 0; p < ins.P; ++p) {
                    for (int d = 0; d < ins.D; ++d) {
                        if (((masks[p] >> d) & 1u) == 0) {
                            if (d != cur[blocked][p] &&
                                (p == 0 || std::abs((int)cur[blocked][p-1] - d) <= 1) &&
                                (p + 1 == ins.P || std::abs((int)cur[blocked][p+1] - d) <= 1))
                                cand.push_back({blocked, p, d});
                        }
                    }
                    int bd = cur[blocked][p];
                    if (cnt[p][bd] == 1) {
                        for (int a: ins.locks[l]) if (cur[a][p] == bd) {
                            for (int d = 0; d < ins.D; ++d) {
                                if (d == bd) continue;
                                if (p > 0 && std::abs((int)cur[a][p-1] - d) > 1) continue;
                                if (p + 1 < ins.P && std::abs((int)cur[a][p+1] - d) > 1) continue;
                                cand.push_back({a, p, d});
                            }
                        }
                    }
                }
                std::vector<std::pair<int,Move>> scored;
                for (auto mv: cand) {
                    auto y = cur;
                    y[mv.k][mv.p] = (uint8_t)mv.d;
                    scored.push_back({eval_cuts(y, nullptr), mv});
                }
                std::sort(scored.begin(), scored.end(), [](auto& a, auto& b){ return a.first < b.first; });
                if ((int)scored.size() > 24) scored.resize(24);
                for (auto sm: scored) {
                    auto mv = sm.second;
                    uint8_t old = cur[mv.k][mv.p];
                    cur[mv.k][mv.p] = (uint8_t)mv.d;
                    if (self(self, cur, depth - 1)) return true;
                    cur[mv.k][mv.p] = old;
                }
                return false;
            };
            if (best <= 5) {
                auto y = bestLocal;
                if (dfs(dfs, y, 5)) return true;
            }
            return false;
        };
        for (int restart = 0; Clock::now() < deadline; ++restart) {
            for (int k = 0; k < ins.K; ++k) x[k] = codes[cd(rng)];
            std::vector<std::pair<int,int>> bad;
            int cur = eval_cuts(x, &bad);
            for (int step = 0; step < 650000 && Clock::now() < deadline; ++step) {
                if (cur < globalBest) {
                    globalBest = cur;
                    bestX = x;
                    std::cout << "c best cell violations " << globalBest
                              << " restart " << restart << " step " << step << "\n" << std::flush;
                    if (cur == 0) { cuts = x; return true; }
                    if (cur <= 3 && try_repair(x)) return true;
                }
                double frac = std::min(1.0, step / 500000.0);
                double temp = std::max(0.03, 5.0 * (1.0 - frac));
                int k;
                if (!bad.empty() && (rng() % 100) < 80) {
                    auto pr = bad[rng() % bad.size()];
                    if ((rng() % 100) < 55) {
                        const auto& a = ins.locks[pr.first];
                        k = a[rng() % a.size()];
                    } else {
                        k = pr.second;
                    }
                } else {
                    k = rng() % ins.K;
                }
                int p = rng() % ins.P;
                std::vector<int> allowed;
                for (int d = 0; d < ins.D; ++d) {
                    if (d == x[k][p]) continue;
                    if (p > 0 && std::abs((int)x[k][p-1] - d) > 1) continue;
                    if (p + 1 < ins.P && std::abs((int)x[k][p+1] - d) > 1) continue;
                    allowed.push_back(d);
                }
                if (allowed.empty()) continue;
                int old = x[k][p];
                int d = allowed[rng() % allowed.size()];
                x[k][p] = (uint8_t)d;
                std::vector<std::pair<int,int>> nb;
                int nv = eval_cuts(x, &nb);
                int delta = nv - cur;
                bool accept = delta <= 0 || unit(rng) < std::exp(-(double)delta / temp);
                if (accept) {
                    cur = nv;
                    bad.swap(nb);
                } else {
                    x[k][p] = (uint8_t)old;
                }
            }
        }
        if (!bestX.empty() && try_repair(bestX)) return true;
        std::cout << "c final best cell violations " << globalBest << "\n";
        return false;
    }

    bool solve(std::vector<std::array<uint8_t,8>>& cuts, double seconds) {
        if (codes.empty()) return false;
        auto deadline = Clock::now() + std::chrono::milliseconds((int)(seconds * 1000));
        std::uniform_int_distribution<int> cd(0, (int)codes.size() - 1);
        std::vector<int> asg(ins.K), bestAsg;
        int globalBest = 1e9;
        for (int restart = 0; Clock::now() < deadline; ++restart) {
            for (int k = 0; k < ins.K; ++k) asg[k] = cd(rng);
            int curSoft = 0, cur = eval(asg, &curSoft);
            for (int step = 0; step < 200000 && Clock::now() < deadline; ++step) {
                if (cur < globalBest) {
                    globalBest = cur; bestAsg = asg;
                    std::cout << "c best high-level violations " << globalBest
                              << " restart " << restart << " step " << step << "\n";
                    if (cur == 0) { cuts.clear(); for (int x: asg) cuts.push_back(codes[x]); return true; }
                }
                std::vector<int> badKeys(ins.K, 0);
                std::vector<std::pair<int,int>> conflicts;
                std::array<uint8_t, 8> masks{};
                for (int l = 0; l < ins.L; ++l) {
                    masks.fill(0);
                    for (int k: ins.locks[l]) {
                        const auto& c = codes[asg[k]];
                        for (int p = 0; p < ins.P; ++p) masks[p] |= (uint8_t)(1u << c[p]);
                    }
                    for (int k: unauth[l]) {
                        const auto& c = codes[asg[k]];
                        bool acc = true;
                        for (int p = 0; p < ins.P; ++p) if (((masks[p] >> c[p]) & 1u) == 0) { acc = false; break; }
                        if (acc) { badKeys[k]++; conflicts.push_back({l, k}); }
                    }
                }
                int k = 0;
                if (!conflicts.empty() && (rng() % 100) < 55) {
                    auto pr = conflicts[rng() % conflicts.size()];
                    if ((rng() % 100) < 65) {
                        const auto& a = ins.locks[pr.first];
                        k = a[rng() % a.size()];
                    } else {
                        k = pr.second;
                    }
                } else if ((rng() % 100) < 85) {
                    int mx = *std::max_element(badKeys.begin(), badKeys.end());
                    std::vector<int> cand;
                    for (int i = 0; i < ins.K; ++i) if (badKeys[i] == mx) cand.push_back(i);
                    k = cand[rng() % cand.size()];
                } else {
                    k = rng() % ins.K;
                }
                int old = asg[k], bestCode = old, bestHard = cur, bestSoft = curSoft;
                int tries = (ins.K <= 60 ? (int)codes.size() : std::min<int>((int)codes.size(), 700));
                for (int it = 0; it < tries; ++it) {
                    int ci = (ins.K <= 60 ? it : cd(rng));
                    asg[k] = ci;
                    int sf = 0, hd = eval(asg, &sf);
                    if (hd < bestHard || (hd == bestHard && sf < bestSoft) ||
                        (hd == bestHard && sf == bestSoft && (rng() % 1000) == 0)) {
                        bestHard = hd; bestSoft = sf; bestCode = ci;
                    }
                }
                if (bestHard >= cur && (rng() % 100) < 5) bestCode = cd(rng);
                asg[k] = bestCode;
                cur = eval(asg, &curSoft);
            }
        }
        if (!bestAsg.empty()) std::cout << "c final best high-level violations " << globalBest << "\n";
        return false;
    }
};

static bool build_model(const Formula& f, const Instance& ins,
                        const std::vector<std::array<uint8_t,8>>& cuts,
                        std::vector<char>& val) {
    val.assign(f.nvars + 1, 0);
    for (int k = 0; k < ins.K; ++k) {
        for (int p = 0; p < ins.P; ++p) {
            int dtrue = cuts[k][p];
            for (int d = 0; d < ins.D; ++d) {
                int v = k * ins.P * ins.D + p * ins.D + d + 1;
                if (v <= f.nvars) val[v] = (d == dtrue);
            }
        }
    }
    int lpd = ins.P * ins.D;
    for (int l = 0; l < ins.L; ++l) {
        for (int p = 0; p < ins.P; ++p) for (int d = 0; d < ins.D; ++d) {
            bool on = false;
            for (int k: ins.locks[l]) if (cuts[k][p] == d) { on = true; break; }
            int v = ins.lockVars[l*lpd + p*ins.D + d];
            if (v <= f.nvars) val[v] = on;
        }
    }
    for (auto& it: ins.blockImp) {
        int b = it.first, kv = it.second.first, lv = it.second.second;
        if (b <= f.nvars && kv > 0 && lv > 0 && kv <= f.nvars && lv <= f.nvars)
            val[b] = val[kv] && !val[lv];
    }
    for (int ci = 0; ci + 1 < (int)f.off.size(); ++ci) {
        bool sat = false;
        for (int j = f.off[ci]; j < f.off[ci+1]; ++j) {
            int lit = f.lits[j], v = std::abs(lit);
            if (v <= f.nvars && ((lit > 0) == (bool)val[v])) { sat = true; break; }
        }
        if (!sat) {
            std::cout << "c internal model check failed at clause " << ci << "\n";
            return false;
        }
    }
    return true;
}

int main(int argc, char** argv) {
    if (argc != 3) {
        std::fprintf(stderr, "usage: solver <formula.cnf> <out.pbp>\n");
        return 1;
    }
    Formula f = parse_cnf(argv[1]);
    Instance ins;
    if (!detect(f, ins)) {
        std::cout << "c unsupported CNF structure\ns UNKNOWN\n";
        return 0;
    }
    std::cout << "c detected lockchart K=" << ins.K << " L=" << ins.L
              << " P=" << ins.P << " D=" << ins.D << "\n";
    if (ins.D == 3 && ins.P == 4) {
        std::cout << "c group3-sized instances are left UNKNOWN without a VeriPB proof\ns UNKNOWN\n";
        return 0;
    }
    Searcher s(ins);
    std::vector<std::array<uint8_t,8>> cuts;
    bool solved = false;
    if (ins.K <= 60) {
        solved = s.solve_cells(cuts, 360.0);
        if (!solved) {
            Searcher s2(ins);
            solved = s2.solve(cuts, 220.0);
        }
    } else {
        solved = s.solve_greedy_repair(cuts, 560.0);
        if (!solved) {
            Searcher s2(ins);
            solved = s2.solve_cells(cuts, 20.0);
        }
    }
    if (!solved) {
        std::cout << "s UNKNOWN\n";
        return 0;
    }
    std::vector<char> model;
    if (!build_model(f, ins, cuts, model)) {
        std::cout << "s UNKNOWN\n";
        return 0;
    }
    std::cout << "s SATISFIABLE\n";
    for (int v = 1; v <= f.nvars; ++v) {
        if ((v - 1) % 20 == 0) std::cout << "v ";
        std::cout << (model[v] ? v : -v) << ' ';
        if (v % 20 == 0) std::cout << "0\n";
    }
    if (f.nvars % 20) std::cout << "0\n";
    return 10;
}
