#include <algorithm>
#include <array>
#include <chrono>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <numeric>
#include <queue>
#include <random>
#include <sstream>
#include <string>
#include <unordered_set>
#include <utility>
#include <vector>

using Clock = std::chrono::steady_clock;

struct Formula {
    int nvars = 0;
    int nclauses_decl = 0;
    std::vector<std::vector<int>> clauses;
};

static bool parse_dimacs(const char* path, Formula& f) {
    std::ifstream in(path);
    if (!in) return false;
    std::string tok;
    std::vector<int> cur;
    while (in >> tok) {
        if (tok == "c") {
            std::string rest;
            std::getline(in, rest);
        } else if (tok == "p") {
            std::string cnf;
            in >> cnf >> f.nvars >> f.nclauses_decl;
        } else {
            int lit = std::stoi(tok);
            if (lit == 0) {
                f.clauses.push_back(cur);
                cur.clear();
            } else {
                cur.push_back(lit);
            }
        }
    }
    return f.nvars > 0 && !f.clauses.empty();
}

static inline int lit_index(int lit) {
    int v = std::abs(lit);
    return lit > 0 ? (v << 1) : ((v << 1) ^ 1);
}

struct Propagator {
    const Formula* formula = nullptr;
    int begin_clause = 0;
    int end_clause = 0;
    std::vector<std::vector<int>> occ;
    std::vector<int8_t> val;
    std::vector<int> units;

    Propagator() = default;
    Propagator(const Formula& f, int begin, int end) { init(f, begin, end); }

    void init(const Formula& f, int begin, int end) {
        formula = &f;
        begin_clause = begin;
        end_clause = end;
        occ.assign((f.nvars + 1) * 2 + 2, {});
        val.assign(f.nvars + 1, 0);
        units.clear();
        for (int ci = begin_clause; ci < end_clause; ++ci) {
            const auto& c = f.clauses[ci];
            if (c.size() == 1) units.push_back(c[0]);
            for (int lit : c) occ[lit_index(lit)].push_back(ci);
        }
    }

    bool assign_lit(int lit, std::queue<int>& q) {
        int v = std::abs(lit);
        int8_t b = lit > 0 ? 1 : -1;
        if (val[v] == b) return true;
        if (val[v] == -b) return false;
        val[v] = b;
        q.push(lit);
        return true;
    }

    bool propagate_base(const std::vector<int>& selected, int n_agents, bool complete_check,
                        std::vector<int8_t>* out = nullptr) {
        std::fill(val.begin(), val.end(), 0);
        std::vector<uint8_t> is_sel(n_agents + 1, 0);
        for (int a : selected) {
            if (a >= 1 && a <= n_agents) is_sel[a] = 1;
        }
        std::queue<int> q;
        for (int a = 1; a <= n_agents; ++a) {
            if (!assign_lit(is_sel[a] ? a : -a, q)) return false;
        }
        for (int lit : units) {
            if (!assign_lit(lit, q)) return false;
        }
        while (!q.empty()) {
            int lit = q.front();
            q.pop();
            int false_lit = -lit;
            int idx = lit_index(false_lit);
            if (idx < 0 || idx >= (int)occ.size()) return false;
            for (int ci : occ[idx]) {
                const auto& c = formula->clauses[ci];
                bool sat = false;
                int un = 0;
                int last = 0;
                for (int x : c) {
                    int8_t xv = val[std::abs(x)];
                    if (xv == 0) {
                        ++un;
                        last = x;
                    } else if ((x > 0 && xv > 0) || (x < 0 && xv < 0)) {
                        sat = true;
                        break;
                    }
                }
                if (sat) continue;
                if (un == 0) return false;
                if (un == 1 && !assign_lit(last, q)) return false;
            }
        }
        if (complete_check) {
            for (int v = 1; v <= formula->nvars; ++v) {
                if (val[v] == 0) val[v] = -1;
            }
            for (int ci = begin_clause; ci < end_clause; ++ci) {
                bool sat = false;
                for (int lit : formula->clauses[ci]) {
                    int8_t xv = val[std::abs(lit)];
                    if ((lit > 0 && xv > 0) || (lit < 0 && xv < 0)) {
                        sat = true;
                        break;
                    }
                }
                if (!sat) return false;
            }
        }
        if (out) *out = val;
        return true;
    }
};

struct DSU {
    std::vector<int> p;
    explicit DSU(int n = 0) { reset(n); }
    void reset(int n) {
        p.resize(n + 1);
        std::iota(p.begin(), p.end(), 0);
    }
    int find(int x) {
        while (p[x] != x) {
            p[x] = p[p[x]];
            x = p[x];
        }
        return x;
    }
    void unite(int a, int b) {
        int ra = find(a), rb = find(b);
        if (ra != rb) p[rb] = ra;
    }
};

struct Skill {
    std::vector<int> agents;
    int req = 0;
};

struct KTF {
    int n_agents = 0;
    std::vector<Skill> skills;
    std::vector<std::pair<int, int>> conflicts;
    int first_positive_unit_clause = -1;
    std::vector<std::vector<int>> comp_vars;
    std::vector<std::vector<int>> comp_costs;
    std::vector<std::vector<std::vector<int>>> comp_cover_lists;
    std::vector<std::vector<int>> skill_comps;
    int budget = -1;
    bool reliable_costs = false;
};

static int infer_n_agents(const Formula& f) {
    for (int lit : f.clauses.front()) {
        if (lit > 0) return lit - 1;
    }
    return 0;
}

static bool extract_ktf(const Formula& f, KTF& ktf) {
    ktf.n_agents = infer_n_agents(f);
    if (ktf.n_agents <= 0 || ktf.n_agents >= f.nvars) return false;

    std::vector<std::pair<int, int>> units;
    for (int i = 0; i < (int)f.clauses.size(); ++i) {
        if (f.clauses[i].size() == 1) units.push_back({i, f.clauses[i][0]});
    }
    int first_pos_unit_idx = -1;
    for (int i = 0; i < (int)units.size(); ++i) {
        if (units[i].second > 0) {
            first_pos_unit_idx = i;
            break;
        }
    }
    if (first_pos_unit_idx <= 0) return false;
    ktf.first_positive_unit_clause = units[first_pos_unit_idx].first;

    int block_begin = 0;
    std::vector<int> aux_pos(f.nvars + 1), aux_neg(f.nvars + 1), touched;
    std::vector<uint8_t> agent_seen(ktf.n_agents + 1, 0);

    for (int ui = 0; ui < first_pos_unit_idx; ++ui) {
        int unit_clause = units[ui].first;
        int unit_lit = units[ui].second;
        if (unit_lit >= 0) return false;
        std::fill(agent_seen.begin(), agent_seen.end(), 0);
        touched.clear();
        for (int ci = block_begin; ci <= unit_clause; ++ci) {
            const auto& c = f.clauses[ci];
            bool is_unit = c.size() == 1;
            for (int lit : c) {
                int v = std::abs(lit);
                if (v <= ktf.n_agents) {
                    agent_seen[v] = 1;
                } else if (!is_unit) {
                    if (aux_pos[v] == 0 && aux_neg[v] == 0) touched.push_back(v);
                    if (lit > 0) ++aux_pos[v];
                    else ++aux_neg[v];
                }
            }
        }
        int u = std::abs(unit_lit);
        std::unordered_set<int> roots;
        roots.reserve(touched.size() * 2 + 1);
        for (int v : touched) {
            if (aux_pos[v] > 0 && aux_neg[v] == 0) roots.insert(v);
        }
        if (!roots.count(u)) return false;
        int s = u, e = u;
        while (roots.count(s - 1)) --s;
        while (roots.count(e + 1)) ++e;
        int root_len = e - s + 1;
        int pos = u - s + 1;

        Skill sk;
        for (int a = 1; a <= ktf.n_agents; ++a) {
            if (agent_seen[a]) sk.agents.push_back(a);
        }
        sk.req = (int)sk.agents.size() - (pos - 1);
        if (sk.req <= 0 || sk.req > (int)sk.agents.size() || root_len < pos) return false;
        ktf.skills.push_back(std::move(sk));

        for (int v : touched) aux_pos[v] = aux_neg[v] = 0;
        block_begin = unit_clause + 1;
    }

    for (const auto& c : f.clauses) {
        if (c.size() == 2 && c[0] < 0 && c[1] < 0 &&
            std::abs(c[0]) <= ktf.n_agents && std::abs(c[1]) <= ktf.n_agents) {
            ktf.conflicts.push_back({std::abs(c[0]), std::abs(c[1])});
        }
    }

    DSU dsu(ktf.n_agents);
    for (auto [a, b] : ktf.conflicts) dsu.unite(a, b);
    std::vector<std::vector<int>> buckets(ktf.n_agents + 1);
    for (int a = 1; a <= ktf.n_agents; ++a) buckets[dsu.find(a)].push_back(a);
    for (auto& b : buckets) {
        if (!b.empty()) {
            std::sort(b.begin(), b.end());
            std::vector<int> opts{0};
            opts.insert(opts.end(), b.begin(), b.end());
            ktf.comp_vars.push_back(std::move(opts));
        }
    }

    std::vector<int> agent_cost(ktf.n_agents + 1, 0);
    std::vector<std::vector<int>> objective_agent_blocks;
    int prev_tail = ktf.first_positive_unit_clause + 1;
    for (int ui = first_pos_unit_idx + 1; ui < (int)units.size(); ++ui) {
        int unit_clause = units[ui].first;
        std::vector<uint8_t> in_block(ktf.n_agents + 1, 0);
        std::vector<int> agents;
        for (int ci = prev_tail; ci <= unit_clause; ++ci) {
            for (int lit : f.clauses[ci]) {
                int v = std::abs(lit);
                if (v <= ktf.n_agents && !in_block[v]) {
                    in_block[v] = 1;
                    agents.push_back(v);
                }
            }
        }
        if (!agents.empty() && units[ui].second < 0) {
            objective_agent_blocks.push_back(std::move(agents));
        }
        prev_tail = unit_clause + 1;
    }
    if (objective_agent_blocks.size() >= 2) {
        for (int bi = 0; bi < (int)objective_agent_blocks.size() && bi < 20; ++bi) {
            int bit = 1 << bi;
            for (int a : objective_agent_blocks[bi]) agent_cost[a] |= bit;
        }
        bool ok = true;
        for (int a = 1; a <= ktf.n_agents; ++a) {
            if (agent_cost[a] <= 0) {
                ok = false;
                break;
            }
        }
        if (ok) {
            ktf.reliable_costs = true;
            int unit_clause = units[first_pos_unit_idx + 1].first;
            int unit_lit = units[first_pos_unit_idx + 1].second;
            std::vector<int> aux_pos(f.nvars + 1), aux_neg(f.nvars + 1), touched;
            for (int ci = ktf.first_positive_unit_clause + 1; ci <= unit_clause; ++ci) {
                if (f.clauses[ci].size() == 1) continue;
                for (int lit : f.clauses[ci]) {
                    int v = std::abs(lit);
                    if (v <= ktf.n_agents) continue;
                    if (aux_pos[v] == 0 && aux_neg[v] == 0) touched.push_back(v);
                    if (lit > 0) ++aux_pos[v];
                    else ++aux_neg[v];
                }
            }
            int u = std::abs(unit_lit);
            std::unordered_set<int> roots;
            roots.reserve(touched.size() * 2 + 1);
            for (int v : touched) {
                if (aux_pos[v] > 0 && aux_neg[v] == 0) roots.insert(v);
            }
            if (roots.count(u)) {
                int s = u;
                while (roots.count(s - 1)) --s;
                ktf.budget = u - s + 1;
            }
        }
    }
    if (!ktf.reliable_costs) {
        std::fill(agent_cost.begin(), agent_cost.end(), 0);
        for (int ci = ktf.first_positive_unit_clause + 1; ci < (int)f.clauses.size(); ++ci) {
            const auto& c = f.clauses[ci];
            if (c.size() != 2) continue;
            for (int lit : c) {
                if (lit < 0 && std::abs(lit) <= ktf.n_agents) ++agent_cost[std::abs(lit)];
            }
        }
        int nonzero_costs = 0;
        for (int a = 1; a <= ktf.n_agents; ++a) {
            if (agent_cost[a] > 0) ++nonzero_costs;
        }
        if (nonzero_costs < ktf.n_agents / 2) {
            int max_len = 1;
            for (const auto& opts : ktf.comp_vars) max_len = std::max<int>(max_len, opts.size() - 1);
            for (const auto& opts : ktf.comp_vars) {
                int len = (int)opts.size() - 1;
                for (int lv = 1; lv < (int)opts.size(); ++lv) {
                    agent_cost[opts[lv]] = max_len - len + lv;
                }
            }
        }
    }
    ktf.comp_costs.reserve(ktf.comp_vars.size());
    for (const auto& opts : ktf.comp_vars) {
        std::vector<int> costs(opts.size(), 0);
        for (int lv = 1; lv < (int)opts.size(); ++lv) costs[lv] = agent_cost[opts[lv]];
        ktf.comp_costs.push_back(std::move(costs));
    }

    std::vector<std::vector<int>> var_skills(ktf.n_agents + 1);
    for (int si = 0; si < (int)ktf.skills.size(); ++si) {
        for (int a : ktf.skills[si].agents) var_skills[a].push_back(si);
    }
    ktf.comp_cover_lists.resize(ktf.comp_vars.size());
    ktf.skill_comps.assign(ktf.skills.size(), {});
    std::vector<uint8_t> skill_has_comp(ktf.skills.size(), 0);
    for (int ci = 0; ci < (int)ktf.comp_vars.size(); ++ci) {
        auto& covers = ktf.comp_cover_lists[ci];
        covers.resize(ktf.comp_vars[ci].size());
        std::fill(skill_has_comp.begin(), skill_has_comp.end(), 0);
        for (int lv = 1; lv < (int)ktf.comp_vars[ci].size(); ++lv) {
            int a = ktf.comp_vars[ci][lv];
            covers[lv] = var_skills[a];
            std::sort(covers[lv].begin(), covers[lv].end());
            covers[lv].erase(std::unique(covers[lv].begin(), covers[lv].end()), covers[lv].end());
            for (int si : covers[lv]) skill_has_comp[si] = 1;
        }
        for (int si = 0; si < (int)ktf.skills.size(); ++si) {
            if (skill_has_comp[si]) ktf.skill_comps[si].push_back(ci);
        }
    }
    return !ktf.skills.empty() && !ktf.comp_vars.empty();
}

struct Solver {
    const Formula& f;
    const KTF& ktf;
    Propagator full_prop;
    Propagator obj_prop;
    Clock::time_point deadline;
    std::mt19937 rng;
    uint64_t nodes = 0;
    uint64_t obj_checks = 0;

    Solver(const Formula& formula, const KTF& k, uint32_t seed, Clock::time_point dl)
        : f(formula), ktf(k), full_prop(formula, 0, (int)formula.clauses.size()),
          obj_prop(formula, k.first_positive_unit_clause + 1, (int)formula.clauses.size()),
          deadline(dl), rng(seed) {}

    bool time_left() const { return Clock::now() < deadline; }

    std::vector<int> selected_from_levels(const std::vector<uint8_t>& level) const {
        std::vector<int> sel;
        sel.reserve(level.size());
        for (int ci = 0; ci < (int)level.size(); ++ci) {
            if (level[ci] > 0) sel.push_back(ktf.comp_vars[ci][level[ci]]);
        }
        return sel;
    }

    bool objective_ok(const std::vector<uint8_t>& level) {
        ++obj_checks;
        if (ktf.reliable_costs && ktf.budget >= 0) {
            int cost = 0;
            for (int ci = 0; ci < (int)level.size(); ++ci) cost += ktf.comp_costs[ci][level[ci]];
            if (cost > ktf.budget) return false;
            return true;
        }
        auto sel = selected_from_levels(level);
        return obj_prop.propagate_base(sel, ktf.n_agents, false, nullptr);
    }

    bool objective_ok_cost(const std::vector<uint8_t>& level, int cost) {
        ++obj_checks;
        if (ktf.reliable_costs && ktf.budget >= 0) return cost <= ktf.budget;
        auto sel = selected_from_levels(level);
        return obj_prop.propagate_base(sel, ktf.n_agents, false, nullptr);
    }

    bool try_full(const std::vector<uint8_t>& level, std::vector<int8_t>& model) {
        auto sel = selected_from_levels(level);
        return full_prop.propagate_base(sel, ktf.n_agents, true, &model);
    }

    int marginal_gain(int ci, int old_lv, int new_lv, const std::vector<int>& deficit) const {
        int gain = 0;
        const auto& oldc = ktf.comp_cover_lists[ci][old_lv];
        const auto& newc = ktf.comp_cover_lists[ci][new_lv];
        size_t i = 0, j = 0;
        while (j < newc.size()) {
            if (i < oldc.size() && oldc[i] == newc[j]) {
                ++i;
                ++j;
            } else if (i < oldc.size() && oldc[i] < newc[j]) {
                ++i;
            } else {
                int si = newc[j++];
                if (deficit[si] > 0) ++gain;
            }
        }
        return gain;
    }

    void apply_upgrade(int ci, int old_lv, int new_lv, std::vector<int>& deficit) const {
        const auto& oldc = ktf.comp_cover_lists[ci][old_lv];
        const auto& newc = ktf.comp_cover_lists[ci][new_lv];
        size_t i = 0, j = 0;
        while (j < newc.size()) {
            if (i < oldc.size() && oldc[i] == newc[j]) {
                ++i;
                ++j;
            } else if (i < oldc.size() && oldc[i] < newc[j]) {
                ++i;
            } else {
                int si = newc[j++];
                --deficit[si];
            }
        }
    }

    bool greedy_attempt(std::vector<int8_t>& model) {
        std::vector<uint8_t> level(ktf.comp_vars.size(), 0);
        std::vector<int> deficit;
        deficit.reserve(ktf.skills.size());
        for (const auto& sk : ktf.skills) deficit.push_back(sk.req);
        int cur_cost = 0;

        for (int step = 0; step < 1000 && time_left(); ++step) {
            if (*std::max_element(deficit.begin(), deficit.end()) <= 0) {
                if (try_full(level, model)) return true;
                return false;
            }
            struct Choice {
                double score;
                int ci;
                int lv;
            };
            std::vector<Choice> choices;
            for (int ci = 0; ci < (int)ktf.comp_vars.size(); ++ci) {
                int cur = level[ci];
                for (int lv = cur + 1; lv < (int)ktf.comp_vars[ci].size(); ++lv) {
                    int gain = marginal_gain(ci, cur, lv, deficit);
                    if (gain <= 0) continue;
                    int dc = ktf.comp_costs[ci][lv] - ktf.comp_costs[ci][cur];
                    if (dc <= 0) dc = lv - cur;
                    double score = (1000.0 * gain) / dc;
                    score += std::uniform_real_distribution<double>(0.0, 30.0)(rng);
                    choices.push_back({score, ci, lv});
                }
            }
            if (choices.empty()) return false;
            std::sort(choices.begin(), choices.end(), [](const Choice& a, const Choice& b) {
                return a.score > b.score;
            });
            int limit = std::min<int>(60, choices.size());
            bool picked = false;
            for (int t = 0; t < limit && time_left(); ++t) {
                int idx = t;
                if (t < 16 && std::uniform_int_distribution<int>(0, 99)(rng) < 35) {
                    idx = std::uniform_int_distribution<int>(0, limit - 1)(rng);
                }
                int ci = choices[idx].ci, lv = choices[idx].lv, old = level[ci];
                int new_cost = cur_cost + ktf.comp_costs[ci][lv] - ktf.comp_costs[ci][old];
                level[ci] = lv;
                if (objective_ok_cost(level, new_cost)) {
                    apply_upgrade(ci, old, lv, deficit);
                    cur_cost = new_cost;
                    picked = true;
                    break;
                }
                level[ci] = old;
            }
            if (!picked) return false;
        }
        return false;
    }

    bool dfs(std::vector<uint8_t>& level, std::vector<int>& deficit, int cur_cost,
             std::vector<int8_t>& model) {
        ++nodes;
        if ((nodes & 2047) == 0 && !time_left()) return false;
        if (*std::max_element(deficit.begin(), deficit.end()) <= 0) {
            return try_full(level, model);
        }
        int best_si = -1;
        int best_count = 1e9;
        std::vector<std::pair<int, int>> best_cands;
        for (int si = 0; si < (int)deficit.size(); ++si) {
            if (deficit[si] <= 0) continue;
            std::vector<std::pair<int, int>> cands;
            std::vector<int> add_costs;
            for (int ci : ktf.skill_comps[si]) {
                int cur = level[ci];
                const auto& curcov = ktf.comp_cover_lists[ci][cur];
                if (std::binary_search(curcov.begin(), curcov.end(), si)) {
                    continue;
                }
                int minlv = -1;
                for (int lv = cur + 1; lv < (int)ktf.comp_vars[ci].size(); ++lv) {
                    const auto& cov = ktf.comp_cover_lists[ci][lv];
                    if (std::binary_search(cov.begin(), cov.end(), si)) {
                        minlv = lv;
                        break;
                    }
                }
                if (minlv >= 0) {
                    cands.push_back({ci, minlv});
                    add_costs.push_back(ktf.comp_costs[ci][minlv] - ktf.comp_costs[ci][cur]);
                }
            }
            if ((int)cands.size() < deficit[si]) return false;
            if (ktf.reliable_costs && ktf.budget >= 0) {
                std::sort(add_costs.begin(), add_costs.end());
                int lb = 0;
                for (int i = 0; i < deficit[si]; ++i) lb += std::max(1, add_costs[i]);
                if (cur_cost + lb > ktf.budget) return false;
            }
            int key = (int)cands.size() * 8 - deficit[si] * 3;
            if (key < best_count) {
                best_count = key;
                best_si = si;
                best_cands.swap(cands);
            }
        }
        if (best_si < 0) return false;

        struct Branch {
            double score;
            int ci;
            int lv;
        };
        std::vector<Branch> branches;
        for (auto [ci, minlv] : best_cands) {
            int cur = level[ci];
            for (int lv = minlv; lv < (int)ktf.comp_vars[ci].size(); ++lv) {
                int gain = marginal_gain(ci, cur, lv, deficit);
                if (gain <= 0) continue;
                int dc = ktf.comp_costs[ci][lv] - ktf.comp_costs[ci][cur];
                if (dc <= 0) dc = lv - cur;
                double score = (1000.0 * gain) / dc + std::uniform_real_distribution<double>(0.0, 5.0)(rng);
                branches.push_back({score, ci, lv});
            }
        }
        std::sort(branches.begin(), branches.end(), [](const Branch& a, const Branch& b) {
            return a.score > b.score;
        });
        if (branches.size() > 96) branches.resize(96);

        for (const auto& br : branches) {
            if (!time_left()) return false;
            int old = level[br.ci];
            int new_cost = cur_cost + ktf.comp_costs[br.ci][br.lv] - ktf.comp_costs[br.ci][old];
            level[br.ci] = br.lv;
            if (!objective_ok_cost(level, new_cost)) {
                level[br.ci] = old;
                continue;
            }
            std::vector<int> nd = deficit;
            apply_upgrade(br.ci, old, br.lv, nd);
            if (dfs(level, nd, new_cost, model)) return true;
            level[br.ci] = old;
        }
        return false;
    }

    bool solve(std::vector<int8_t>& model) {
        for (int i = 0; i < 200 && time_left(); ++i) {
            if (greedy_attempt(model)) return true;
        }
        while (time_left()) {
            std::vector<uint8_t> level(ktf.comp_vars.size(), 0);
            std::vector<int> deficit;
            for (const auto& sk : ktf.skills) deficit.push_back(sk.req);
            if (dfs(level, deficit, 0, model)) return true;
            // A different seed changes branch tie-breaking.
            rng.seed(rng() + 0x9e3779b9U);
            if (nodes > 50000000) break;
        }
        return false;
    }
};

struct CDCL {
    int nvars = 0;
    int decision_vars = 0;
    std::vector<std::vector<int>> clauses;
    std::vector<std::array<int, 2>> watches;
    std::vector<std::vector<int>> watch_list;
    std::vector<int8_t> val;
    std::vector<int> lev;
    std::vector<int> reason;
    std::vector<int> trail;
    std::vector<int> trail_lim;
    int qhead = 0;
    std::vector<double> activity;
    double var_inc = 1.0;
    double var_decay = 0.95;
    std::ofstream proof;
    Clock::time_point deadline;
    uint64_t conflicts = 0;

    CDCL(const Formula& f, int decision_limit, const char* proof_path, Clock::time_point dl)
        : nvars(f.nvars), decision_vars(decision_limit), deadline(dl) {
        clauses = f.clauses;
        watches.resize(clauses.size());
        watch_list.assign((nvars + 1) * 2 + 2, {});
        val.assign(nvars + 1, 0);
        lev.assign(nvars + 1, 0);
        reason.assign(nvars + 1, -1);
        activity.assign(nvars + 1, 0.0);
        for (int ci = 0; ci < (int)clauses.size(); ++ci) attach_clause(ci);
        for (const auto& c : clauses) {
            for (int lit : c) activity[std::abs(lit)] += 1.0;
        }
        proof.open(proof_path, std::ios::out | std::ios::trunc);
        if (proof) proof << "pseudo-Boolean proof version 3.0\n";
    }

    int decision_level() const { return (int)trail_lim.size(); }
    bool time_left() const { return Clock::now() < deadline; }

    int lit_val(int lit) const {
        int8_t v = val[std::abs(lit)];
        if (v == 0) return 0;
        return (lit > 0) == (v > 0) ? 1 : -1;
    }

    bool enqueue(int lit, int rsn) {
        int v = std::abs(lit);
        int8_t b = lit > 0 ? 1 : -1;
        if (val[v] == b) return true;
        if (val[v] == -b) return false;
        val[v] = b;
        lev[v] = decision_level();
        reason[v] = rsn;
        trail.push_back(lit);
        return true;
    }

    void attach_clause(int ci) {
        auto& c = clauses[ci];
        if (c.empty()) {
            watches[ci] = {0, 0};
            return;
        }
        if (c.size() == 1) {
            watches[ci] = {0, 0};
            watch_list[lit_index(c[0])].push_back(ci);
            return;
        }
        watches[ci] = {0, 1};
        watch_list[lit_index(c[0])].push_back(ci);
        watch_list[lit_index(c[1])].push_back(ci);
    }

    int propagate() {
        while (qhead < (int)trail.size()) {
            int lit = trail[qhead++];
            int false_idx = lit_index(-lit);
            auto& ws = watch_list[false_idx];
            int i = 0;
            int out = 0;
            while (i < (int)ws.size()) {
                int ci = ws[i++];
                auto& c = clauses[ci];
                if (c.empty()) {
                    ws[out++] = ci;
                    while (i < (int)ws.size()) ws[out++] = ws[i++];
                    ws.resize(out);
                    return ci;
                }
                int wi = watches[ci][0];
                int wj = watches[ci][1];
                if (c[wi] != -lit) std::swap(wi, wj);
                if (c[wi] != -lit) {
                    // Stale watch entry.
                    ws[out++] = ci;
                    continue;
                }
                int other = c[wj];
                if (lit_val(other) == 1) {
                    ws[out++] = ci;
                    continue;
                }
                bool found = false;
                for (int k = 0; k < (int)c.size(); ++k) {
                    if (k == wi || k == wj) continue;
                    if (lit_val(c[k]) != -1) {
                        if (watches[ci][0] == wi) watches[ci][0] = k;
                        else watches[ci][1] = k;
                        watch_list[lit_index(c[k])].push_back(ci);
                        found = true;
                        break;
                    }
                }
                if (found) continue;
                ws[out++] = ci;
                if (lit_val(other) == -1) {
                    while (i < (int)ws.size()) ws[out++] = ws[i++];
                    ws.resize(out);
                    return ci;
                }
                if (!enqueue(other, ci)) {
                    while (i < (int)ws.size()) ws[out++] = ws[i++];
                    ws.resize(out);
                    return ci;
                }
            }
            ws.resize(out);
        }
        return -1;
    }

    void new_decision_level() { trail_lim.push_back((int)trail.size()); }

    void backtrack(int lvl) {
        if (decision_level() <= lvl) return;
        int target = trail_lim[lvl];
        for (int i = (int)trail.size() - 1; i >= target; --i) {
            int v = std::abs(trail[i]);
            val[v] = 0;
            lev[v] = 0;
            reason[v] = -1;
        }
        trail.resize(target);
        qhead = target;
        trail_lim.resize(lvl);
    }

    void bump_clause(const std::vector<int>& c) {
        for (int lit : c) activity[std::abs(lit)] += var_inc;
        var_inc /= var_decay;
        if (var_inc > 1e100) {
            for (double& a : activity) a *= 1e-100;
            var_inc *= 1e-100;
        }
    }

    int pick_branch_lit() {
        int best = 0;
        double ba = -1.0;
        for (int v = 1; v <= decision_vars; ++v) {
            if (val[v] == 0 && activity[v] > ba) {
                ba = activity[v];
                best = v;
            }
        }
        return best;
    }

    void write_rup_clause(const std::vector<int>& c) {
        if (!proof) return;
        proof << "rup ";
        for (int lit : c) {
            proof << "1 ";
            if (lit < 0) proof << "~";
            proof << "x" << std::abs(lit) << " ";
        }
        proof << ">= 1 ;\n";
    }

    int add_learned(std::vector<int> c) {
        // Remove duplicates while preserving the asserting literal at c[0].
        std::vector<int> out;
        out.reserve(c.size());
        std::vector<int8_t> seen(nvars + 1, 0);
        for (int lit : c) {
            int v = std::abs(lit);
            int8_t s = lit > 0 ? 1 : -1;
            if (seen[v] == s) continue;
            if (seen[v] == -s) return -1;
            seen[v] = s;
            out.push_back(lit);
        }
        c.swap(out);
        write_rup_clause(c);
        int ci = (int)clauses.size();
        clauses.push_back(c);
        watches.push_back({0, c.size() > 1 ? 1 : 0});
        attach_clause(ci);
        bump_clause(c);
        return ci;
    }

    void analyze(int confl, std::vector<int>& learnt, int& backtrack_level) {
        std::vector<uint8_t> seen(nvars + 1, 0);
        learnt.clear();
        learnt.push_back(0);
        int path_count = 0;
        int p = 0;
        int idx = (int)trail.size() - 1;
        int ci = confl;
        do {
            const auto& c = clauses[ci];
            for (int lit : c) {
                int v = std::abs(lit);
                if (seen[v] || lev[v] == 0) continue;
                seen[v] = 1;
                activity[v] += var_inc;
                if (lev[v] == decision_level()) ++path_count;
                else learnt.push_back(lit);
            }
            while (idx >= 0 && !seen[std::abs(trail[idx])]) --idx;
            p = trail[idx--];
            ci = reason[std::abs(p)];
            seen[std::abs(p)] = 0;
            --path_count;
        } while (path_count > 0 && ci >= 0);
        learnt[0] = -p;
        project_auxiliaries(learnt);
        if (learnt.empty()) {
            backtrack_level = 0;
            return;
        }
        int best = 0;
        for (int i = 1; i < (int)learnt.size(); ++i) {
            if (lev[std::abs(learnt[i])] > lev[std::abs(learnt[best])]) best = i;
        }
        std::swap(learnt[0], learnt[best]);
        backtrack_level = 0;
        for (int i = 1; i < (int)learnt.size(); ++i) {
            backtrack_level = std::max(backtrack_level, lev[std::abs(learnt[i])]);
        }
    }

    void project_auxiliaries(std::vector<int>& clause) {
        std::vector<int8_t> present(nvars + 1, 0);
        std::vector<int> work = clause;
        clause.clear();
        auto add_lit = [&](int lit, std::vector<int>& dst) -> bool {
            int v = std::abs(lit);
            int8_t s = lit > 0 ? 1 : -1;
            if (present[v] == s) return true;
            if (present[v] == -s) return false;
            present[v] = s;
            dst.push_back(lit);
            return true;
        };
        bool changed = true;
        int rounds = 0;
        while (changed && rounds++ < 10000) {
            changed = false;
            std::fill(present.begin(), present.end(), 0);
            std::vector<int> next;
            next.reserve(work.size() + 8);
            for (int lit : work) {
                int v = std::abs(lit);
                if (v > decision_vars && reason[v] >= 0) {
                    int need = -lit;
                    const auto& r = clauses[reason[v]];
                    for (int y : r) {
                        if (y == need) continue;
                        if (!add_lit(y, next)) {
                            clause = work;
                            return;
                        }
                    }
                    changed = true;
                } else {
                    if (!add_lit(lit, next)) {
                        clause = work;
                        return;
                    }
                }
            }
            work.swap(next);
            if (work.size() > 2000) break;
        }
        clause.swap(work);
    }

    enum Result { UNKNOWN, SAT, UNSAT };

    Result solve(std::vector<int8_t>& model) {
        for (int ci = 0; ci < (int)clauses.size(); ++ci) {
            if (clauses[ci].empty()) {
                write_rup_clause({});
                finish_unsat_proof();
                return UNSAT;
            }
            if (clauses[ci].size() == 1 && !enqueue(clauses[ci][0], ci)) {
                write_rup_clause({});
                finish_unsat_proof();
                return UNSAT;
            }
        }
        int confl = propagate();
        if (confl >= 0) {
            write_rup_clause({});
            finish_unsat_proof();
            return UNSAT;
        }

        int restart_limit = 100;
        while (time_left()) {
            confl = propagate();
            if (confl >= 0) {
                ++conflicts;
                if (decision_level() == 0) {
                    write_rup_clause({});
                    finish_unsat_proof();
                    return UNSAT;
                }
                std::vector<int> learnt;
                int bt = 0;
                analyze(confl, learnt, bt);
                int lci = add_learned(learnt);
                if (lci < 0) return UNKNOWN;
                backtrack(bt);
                if (!enqueue(clauses[lci][0], lci)) {
                    write_rup_clause({});
                    finish_unsat_proof();
                    return UNSAT;
                }
                if ((int)conflicts >= restart_limit) {
                    backtrack(0);
                    restart_limit = (int)(restart_limit * 1.5) + 100;
                }
            } else {
                int next = pick_branch_lit();
                if (next == 0) {
                    // The high-level variables are complete. This fallback is
                    // mainly for UNSAT proof search; leave SAT certification to
                    // the structural solver unless propagation already assigned
                    // everything.
                    bool all_assigned = true;
                    for (int v = 1; v <= nvars; ++v) {
                        if (val[v] == 0) {
                            all_assigned = false;
                            break;
                        }
                    }
                    if (all_assigned) {
                        model = val;
                        return SAT;
                    }
                    return UNKNOWN;
                }
                new_decision_level();
                // Prefer false first on these encodings; selected-agent true is expensive.
                enqueue(-next, -1);
            }
        }
        return UNKNOWN;
    }

    void finish_unsat_proof() {
        if (!proof) return;
        proof << "output NONE ;\n";
        proof << "conclusion UNSAT : -1 ;\n";
        proof << "end pseudo-Boolean proof ;\n";
        proof.flush();
    }
};

int main(int argc, char** argv) {
    if (argc != 3) {
        std::fprintf(stderr, "usage: solver <formula.cnf> <out.pbp>\n");
        return 1;
    }
    Formula f;
    if (!parse_dimacs(argv[1], f)) {
        std::printf("s UNKNOWN\n");
        return 0;
    }
    KTF ktf;
    if (!extract_ktf(f, ktf)) {
        std::printf("s UNKNOWN\n");
        return 0;
    }

    auto final_deadline = Clock::now() + std::chrono::seconds(575);
    std::vector<int8_t> model;
    {
        auto sat_deadline = Clock::now() + std::chrono::seconds(540);
        if (sat_deadline > final_deadline) sat_deadline = final_deadline;
        Solver solver(f, ktf, 1234567u, sat_deadline);
        if (solver.solve(model)) {
            std::printf("s SATISFIABLE\n");
            std::printf("v");
            for (int v = 1; v <= f.nvars; ++v) {
                int lit = model[v] > 0 ? v : -v;
                std::printf(" %d", lit);
                if (v % 20 == 0) std::printf("\nv");
            }
            std::printf(" 0\n");
            return 10;
        }
    }

    CDCL cdcl(f, ktf.n_agents, argv[2], final_deadline);
    auto cres = cdcl.solve(model);
    if (cres == CDCL::UNSAT) {
        std::printf("s UNSATISFIABLE\n");
        return 20;
    }
    if (cres == CDCL::SAT) {
        bool ok = true;
        for (const auto& c : f.clauses) {
            bool sat = false;
            for (int lit : c) {
                int8_t xv = model[std::abs(lit)];
                if ((lit > 0 && xv > 0) || (lit < 0 && xv < 0)) {
                    sat = true;
                    break;
                }
            }
            if (!sat) {
                ok = false;
                break;
            }
        }
        if (ok) {
            std::printf("s SATISFIABLE\n");
            std::printf("v");
            for (int v = 1; v <= f.nvars; ++v) {
                int lit = model[v] > 0 ? v : -v;
                std::printf(" %d", lit);
                if (v % 20 == 0) std::printf("\nv");
            }
            std::printf(" 0\n");
            return 10;
        }
    }
    std::printf("s UNKNOWN\n");
    return 0;
}
