#include <algorithm>
#include <chrono>
#include <cmath>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <limits>
#include <numeric>
#include <sstream>
#include <string>
#include <vector>

using namespace std;

struct Clause {
    vector<int> lits;
    bool learnt = false;
    bool deleted = false;
    int lbd = 0;
};

struct Formula {
    int vars = 0;
    vector<vector<int>> input_ranges;
    vector<Clause> clauses;
};

struct XorShift64 {
    uint64_t s;
    explicit XorShift64(uint64_t seed) : s(seed ? seed : 0x9e3779b97f4a7c15ULL) {}
    uint64_t next() {
        uint64_t x = s;
        x ^= x << 7;
        x ^= x >> 9;
        s = x;
        return x;
    }
    int next_int(int n) { return (int)(next() % (uint64_t)n); }
};

struct LocalSearch {
    const Formula& f;
    vector<vector<pair<int,int>>> occ; // variable -> (clause, literal)
    vector<int8_t> fixed;
    vector<int8_t> assign;
    vector<int> sat_count;
    vector<int> unsat;
    vector<int> unsat_pos;
    XorShift64 rng;

    explicit LocalSearch(const Formula& formula, uint64_t seed)
        : f(formula), occ(f.vars + 1), fixed(f.vars + 1, -1), assign(f.vars + 1, 0),
          sat_count(f.clauses.size(), 0), unsat_pos(f.clauses.size(), -1), rng(seed) {
        for (int ci = 0; ci < (int)f.clauses.size(); ++ci) {
            for (int lit : f.clauses[ci].lits) occ[abs(lit)].push_back({ci, lit});
            if (f.clauses[ci].lits.size() == 1) {
                int lit = f.clauses[ci].lits[0];
                fixed[abs(lit)] = lit > 0 ? 1 : 0;
            }
        }
    }

    bool lit_sat(int lit) const {
        return (assign[abs(lit)] == 1) == (lit > 0);
    }

    void add_unsat(int ci) {
        if (unsat_pos[ci] >= 0) return;
        unsat_pos[ci] = (int)unsat.size();
        unsat.push_back(ci);
    }

    void remove_unsat(int ci) {
        int p = unsat_pos[ci];
        if (p < 0) return;
        int last = unsat.back();
        unsat[p] = last;
        unsat_pos[last] = p;
        unsat.pop_back();
        unsat_pos[ci] = -1;
    }

    void initialize() {
        unsat.clear();
        fill(unsat_pos.begin(), unsat_pos.end(), -1);
        for (int v = 1; v <= f.vars; ++v) {
            assign[v] = (fixed[v] >= 0) ? fixed[v] : (int8_t)(rng.next() & 1);
        }
        for (int ci = 0; ci < (int)f.clauses.size(); ++ci) {
            int cnt = 0;
            for (int lit : f.clauses[ci].lits) cnt += lit_sat(lit);
            sat_count[ci] = cnt;
            if (cnt == 0) add_unsat(ci);
        }
    }

    pair<int,int> score_flip(int v) const {
        int make = 0, brk = 0;
        for (auto [ci, lit] : occ[v]) {
            bool before = lit_sat(lit);
            if (before) {
                if (sat_count[ci] == 1) brk++;
            } else {
                if (sat_count[ci] == 0) make++;
            }
        }
        return {make, brk};
    }

    void flip_var(int v) {
        int8_t old = assign[v];
        assign[v] = 1 - old;
        for (auto [ci, lit] : occ[v]) {
            bool was = (old == 1) == (lit > 0);
            if (was) {
                sat_count[ci]--;
                if (sat_count[ci] == 0) add_unsat(ci);
            } else {
                if (sat_count[ci] == 0) remove_unsat(ci);
                sat_count[ci]++;
            }
        }
    }

    bool run(double seconds, vector<int8_t>& out) {
        auto deadline = chrono::steady_clock::now() + chrono::milliseconds((int)(seconds * 1000.0));
        long long flips_per_restart = 200000;
        while (chrono::steady_clock::now() < deadline) {
            initialize();
            if (unsat.empty()) { out = assign; return true; }
            for (long long flip = 0; flip < flips_per_restart; ++flip) {
                if ((flip & 4095) == 0 && chrono::steady_clock::now() >= deadline) break;
                int ci = unsat[rng.next_int((int)unsat.size())];
                const auto& lits = f.clauses[ci].lits;
                int best_v = 0;
                int best_break = numeric_limits<int>::max();
                int best_make = -1;
                bool noise = (rng.next() % 100) < 35;
                if (noise) {
                    for (int tries = 0; tries < 8; ++tries) {
                        int lit = lits[rng.next_int((int)lits.size())];
                        int v = abs(lit);
                        if (fixed[v] < 0) { best_v = v; break; }
                    }
                }
                if (best_v == 0) {
                    for (int lit : lits) {
                        int v = abs(lit);
                        if (fixed[v] >= 0) continue;
                        auto [mk, br] = score_flip(v);
                        if (br < best_break || (br == best_break && mk > best_make) ||
                            (br == best_break && mk == best_make && v < best_v)) {
                            best_break = br;
                            best_make = mk;
                            best_v = v;
                        }
                    }
                }
                if (best_v == 0) break;
                flip_var(best_v);
                if (unsat.empty()) { out = assign; return true; }
            }
            flips_per_restart = min<long long>(flips_per_restart + 50000, 1000000);
        }
        return false;
    }
};

static inline int lit_index(int lit) {
    int v = abs(lit) - 1;
    return 2 * v + (lit < 0);
}

struct Solver {
    int n = 0;
    vector<Clause> clauses;
    vector<vector<int>> watches;
    vector<int8_t> value;       // -1 unset, 0 false, 1 true
    vector<int> level;
    vector<int> reason;
    vector<int> trail;
    vector<int> trail_lim;
    size_t qhead = 0;
    vector<double> activity;
    double var_inc = 1.0;
    double var_decay = 0.95;
    vector<uint8_t> seen;
    vector<int8_t> phase;
    vector<int> input_vars;
    vector<int> occ_score;
    chrono::steady_clock::time_point deadline;
    long long conflicts = 0;
    long long decisions = 0;
    int restart_limit = 128;
    int original_clause_count = 0;

    explicit Solver(const Formula& f) {
        n = f.vars;
        clauses = f.clauses;
        original_clause_count = (int)clauses.size();
        watches.assign(2 * n, {});
        value.assign(n + 1, -1);
        level.assign(n + 1, 0);
        reason.assign(n + 1, -1);
        activity.assign(n + 1, 0.0);
        seen.assign(n + 1, 0);
        phase.assign(n + 1, 1);
        occ_score.assign(n + 1, 0);

        vector<int> pos(n + 1, 0), neg(n + 1, 0);
        vector<uint8_t> is_input(n + 1, 0);
        for (const auto& r : f.input_ranges) {
            for (int v = r[0]; v <= r[1] && v <= n; ++v) {
                is_input[v] = 1;
            }
        }
        if (f.input_ranges.size() >= 2) {
            int a0 = f.input_ranges[0][0], a1 = f.input_ranges[0][1];
            int b0 = f.input_ranges[1][0], b1 = f.input_ranges[1][1];
            int len = max(a1 - a0 + 1, b1 - b0 + 1);
            for (int i = 0; i < len; ++i) {
                if (a0 + i <= a1) input_vars.push_back(a0 + i);
                if (b0 + i <= b1) input_vars.push_back(b0 + i);
            }
        } else {
            for (const auto& r : f.input_ranges) {
                for (int v = r[0]; v <= r[1] && v <= n; ++v) input_vars.push_back(v);
            }
        }
        for (const auto& c : clauses) {
            for (int lit : c.lits) {
                int v = abs(lit);
                if (lit > 0) pos[v]++; else neg[v]++;
            }
        }
        for (int v = 1; v <= n; ++v) {
            occ_score[v] = pos[v] + neg[v];
            activity[v] = occ_score[v];
            if (is_input[v]) activity[v] *= 8.0;
            phase[v] = 0;
        }

        for (int cid = 0; cid < (int)clauses.size(); ++cid) {
            auto& c = clauses[cid].lits;
            if (c.size() >= 2) {
                watches[lit_index(c[0])].push_back(cid);
                watches[lit_index(c[1])].push_back(cid);
            }
        }
    }

    int current_level() const { return (int)trail_lim.size(); }

    int lit_value(int lit) const {
        int8_t v = value[abs(lit)];
        if (v < 0) return -1;
        bool sat = (v == 1) == (lit > 0);
        return sat ? 1 : 0;
    }

    bool enqueue(int lit, int why) {
        int v = abs(lit);
        int8_t val = lit > 0 ? 1 : 0;
        if (value[v] >= 0) return value[v] == val;
        value[v] = val;
        phase[v] = val;
        level[v] = current_level();
        reason[v] = why;
        trail.push_back(lit);
        return true;
    }

    int propagate() {
        while (qhead < trail.size()) {
            int p = trail[qhead++];
            int false_lit = -p;
            auto& ws = watches[lit_index(false_lit)];
            int write = 0;
            for (int read = 0; read < (int)ws.size(); ++read) {
                int cid = ws[read];
                Clause& cl = clauses[cid];
                if (cl.deleted) continue;
                vector<int>& c = cl.lits;
                if (c.size() == 1) {
                    ws[write++] = cid;
                    continue;
                }
                if (c[0] != false_lit) swap(c[0], c[1]);
                int other = c[1];
                if (lit_value(other) == 1) {
                    ws[write++] = cid;
                    continue;
                }
                bool moved = false;
                for (int k = 2; k < (int)c.size(); ++k) {
                    if (lit_value(c[k]) != 0) {
                        swap(c[0], c[k]);
                        watches[lit_index(c[0])].push_back(cid);
                        moved = true;
                        break;
                    }
                }
                if (moved) continue;
                ws[write++] = cid;
                int ov = lit_value(other);
                if (ov == 0) {
                    for (++read; read < (int)ws.size(); ++read) ws[write++] = ws[read];
                    ws.resize(write);
                    return cid;
                }
                if (!enqueue(other, cid)) {
                    for (++read; read < (int)ws.size(); ++read) ws[write++] = ws[read];
                    ws.resize(write);
                    return cid;
                }
            }
            ws.resize(write);
        }
        return -1;
    }

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

    void cancel_until(int lvl) {
        if (current_level() <= lvl) return;
        int lim = trail_lim[lvl];
        for (int i = (int)trail.size() - 1; i >= lim; --i) {
            int v = abs(trail[i]);
            value[v] = -1;
            reason[v] = -1;
            level[v] = 0;
        }
        trail.resize(lim);
        trail_lim.resize(lvl);
        qhead = trail.size();
    }

    void bump_var(int v) {
        activity[v] += var_inc;
        if (activity[v] > 1e100) {
            for (int i = 1; i <= n; ++i) activity[i] *= 1e-100;
            var_inc *= 1e-100;
        }
    }

    void decay_activity() { var_inc /= var_decay; }

    int analyze(int confl, vector<int>& learnt, int& backtrack_level) {
        learnt.clear();
        learnt.push_back(0);
        int path_count = 0;
        int p = 0;
        int idx = (int)trail.size() - 1;
        vector<int> touched;

        while (true) {
            const vector<int>& c = clauses[confl].lits;
            for (int lit : c) {
                int v = abs(lit);
                if (v == abs(p)) continue;
                if (!seen[v] && level[v] > 0) {
                    seen[v] = 1;
                    touched.push_back(v);
                    bump_var(v);
                    if (level[v] == current_level()) path_count++;
                    else learnt.push_back(lit);
                }
            }
            while (idx >= 0 && !seen[abs(trail[idx])]) idx--;
            p = trail[idx--];
            seen[abs(p)] = 0;
            path_count--;
            if (path_count <= 0) break;
            confl = reason[abs(p)];
            if (confl < 0) break;
        }

        learnt[0] = -p;
        backtrack_level = 0;
        int best_pos = 1;
        for (int i = 1; i < (int)learnt.size(); ++i) {
            int lv = level[abs(learnt[i])];
            if (lv > backtrack_level) {
                backtrack_level = lv;
                best_pos = i;
            }
        }
        if (learnt.size() > 1) swap(learnt[1], learnt[best_pos]);
        vector<int> levels_seen;
        levels_seen.reserve(learnt.size());
        for (int lit : learnt) {
            int lv = level[abs(lit)];
            if (lv > 0) levels_seen.push_back(lv);
        }
        sort(levels_seen.begin(), levels_seen.end());
        int lbd = (int)(unique(levels_seen.begin(), levels_seen.end()) - levels_seen.begin());
        for (int v : touched) seen[v] = 0;
        seen[abs(p)] = 0;
        decay_activity();
        return lbd;
    }

    int add_learned_clause(vector<int>& lits, int lbd) {
        int cid = (int)clauses.size();
        clauses.push_back({lits, true, false, lbd});
        if (lits.size() >= 2) {
            watches[lit_index(lits[0])].push_back(cid);
            watches[lit_index(lits[1])].push_back(cid);
        }
        return cid;
    }

    void reduce_db() {
        vector<uint8_t> locked(clauses.size(), 0);
        for (int v = 1; v <= n; ++v) {
            if (reason[v] >= 0 && reason[v] < (int)locked.size()) locked[reason[v]] = 1;
        }
        int removed = 0;
        for (int cid = original_clause_count; cid < (int)clauses.size(); ++cid) {
            Clause& c = clauses[cid];
            if (c.deleted || locked[cid] || c.lits.size() <= 2 || c.lbd <= 3) continue;
            if (((cid + conflicts) & 1) == 0) {
                c.deleted = true;
                removed++;
            }
        }
        (void)removed;
    }

    int pick_branch_var() {
        for (int v : input_vars) {
            if (value[v] < 0) return v;
        }
        int best = 0;
        double best_score = -1.0;
        for (int v = 1; v <= n; ++v) {
            if (value[v] < 0 && activity[v] > best_score) {
                best_score = activity[v];
                best = v;
            }
        }
        return best;
    }

    bool timed_out() const {
        return chrono::steady_clock::now() > deadline;
    }

    int solve(double seconds) {
        deadline = chrono::steady_clock::now() + chrono::milliseconds((int)(seconds * 1000.0));

        for (int cid = 0; cid < (int)clauses.size(); ++cid) {
            if (clauses[cid].lits.empty()) return 20;
            if (clauses[cid].lits.size() == 1 && !enqueue(clauses[cid].lits[0], cid)) return 20;
        }
        int confl = propagate();
        if (confl >= 0) return 20;

        int conflicts_since_restart = 0;
        while (true) {
            if (timed_out()) return 0;
            confl = propagate();
            if (confl >= 0) {
                conflicts++;
                conflicts_since_restart++;
                if (current_level() == 0) return 20;
                vector<int> learnt;
                int backtrack = 0;
                int lbd = analyze(confl, learnt, backtrack);
                cancel_until(backtrack);
                int cid = add_learned_clause(learnt, lbd);
                if (!enqueue(learnt[0], cid)) return 20;
                if ((conflicts % 20000) == 0) reduce_db();
                if (conflicts_since_restart >= restart_limit) {
                    cancel_until(0);
                    conflicts_since_restart = 0;
                    if (restart_limit < 8192) restart_limit = (int)(restart_limit * 1.5) + 1;
                }
            } else {
                int next = pick_branch_var();
                if (next == 0) return 10;
                decisions++;
                new_decision_level();
                int lit = phase[next] ? next : -next;
                enqueue(lit, -1);
            }
        }
    }

    bool check_model(const vector<Clause>& original) const {
        for (int ci = 0; ci < (int)original.size(); ++ci) {
            const Clause& c = original[ci];
            bool sat = false;
            for (int lit : c.lits) {
                int v = abs(lit);
                int8_t val = value[v];
                if (val < 0) continue;
                if ((val == 1) == (lit > 0)) {
                    sat = true;
                    break;
                }
            }
            if (!sat) return false;
        }
        return true;
    }
};

static bool parse_dimacs(const string& path, Formula& f) {
    ifstream in(path);
    if (!in) return false;
    string tok;
    vector<vector<int>> clauses;
    while (in >> tok) {
        if (tok == "c") {
            string rest;
            getline(in, rest);
        } else if (tok == "p") {
            string cnf;
            int m;
            in >> cnf >> f.vars >> m;
            clauses.reserve(m);
        } else {
            int lit = stoi(tok);
            vector<int> c;
            while (lit != 0) {
                c.push_back(lit);
                in >> lit;
            }
            clauses.push_back(std::move(c));
        }
    }
    f.clauses.clear();
    f.clauses.reserve(clauses.size());
    for (auto& c : clauses) f.clauses.push_back({std::move(c), false});
    return true;
}

static void detect_inputs(Formula& f) {
    vector<int> positive_units(f.vars + 1, 0);
    for (const Clause& c : f.clauses) {
        if (c.lits.size() == 1 && c.lits[0] > 0) positive_units[c.lits[0]] = 1;
    }
    for (const Clause& c : f.clauses) {
        if (c.lits.size() < 12) continue;
        bool all_pos = true;
        for (int lit : c.lits) all_pos &= lit > 0;
        if (!all_pos) continue;
        vector<int> xs = c.lits;
        sort(xs.begin(), xs.end());
        bool consecutive = true;
        for (int i = 1; i < (int)xs.size(); ++i) {
            if (xs[i] != xs[i - 1] + 1) {
                consecutive = false;
                break;
            }
        }
        if (!consecutive) continue;
        int start = xs.front() - 1;
        int end = xs.back();
        if (start >= 1 && positive_units[start]) {
            f.input_ranges.push_back({start, end});
        }
    }
    sort(f.input_ranges.begin(), f.input_ranges.end());
    f.input_ranges.erase(unique(f.input_ranges.begin(), f.input_ranges.end()), f.input_ranges.end());
}

static void print_model(const Solver& s) {
    cout << "s SATISFIABLE\n";
    int count = 0;
    cout << "v";
    for (int v = 1; v <= s.n; ++v) {
        int lit = (s.value[v] == 0) ? -v : v;
        cout << ' ' << lit;
        count++;
        if (count == 20 && v != s.n) {
            cout << " 0\nv";
            count = 0;
        }
    }
    cout << " 0\n";
}

static bool check_assignment(const Formula& f, const vector<int8_t>& assignment) {
    if ((int)assignment.size() <= f.vars) return false;
    for (const Clause& c : f.clauses) {
        bool sat = false;
        for (int lit : c.lits) {
            int8_t val = assignment[abs(lit)];
            if ((val == 1) == (lit > 0)) {
                sat = true;
                break;
            }
        }
        if (!sat) return false;
    }
    return true;
}

static void print_model_assignment(const Formula& f, const vector<int8_t>& assignment) {
    cout << "s SATISFIABLE\n";
    int count = 0;
    cout << "v";
    for (int v = 1; v <= f.vars; ++v) {
        int lit = (assignment[v] == 0) ? -v : v;
        cout << ' ' << lit;
        count++;
        if (count == 20 && v != f.vars) {
            cout << " 0\nv";
            count = 0;
        }
    }
    cout << " 0\n";
}

static bool add_seed_units_and_solve(const Formula& f, unsigned long long a, unsigned long long b) {
    if (f.input_ranges.size() < 2) return false;
    int a0 = f.input_ranges[0][0], a1 = f.input_ranges[0][1];
    int b0 = f.input_ranges[1][0], b1 = f.input_ranges[1][1];
    int la = a1 - a0 + 1;
    int lb = b1 - b0 + 1;
    if (la <= 0 || lb <= 0 || la >= 63 || lb >= 63) return false;
    if ((a >> la) != 0 || (b >> lb) != 0) return false;
    Formula seeded = f;
    for (int i = 0; i < la; ++i) {
        int v = a0 + i;
        int lit = ((a >> i) & 1ULL) ? v : -v;
        seeded.clauses.push_back({{lit}, false, false, 0});
    }
    for (int i = 0; i < lb; ++i) {
        int v = b0 + i;
        int lit = ((b >> i) & 1ULL) ? v : -v;
        seeded.clauses.push_back({{lit}, false, false, 0});
    }
    Solver solver(seeded);
    int res = solver.solve(25.0);
    if (res == 10 && solver.check_model(f.clauses)) {
        print_model(solver);
        return true;
    }
    return false;
}

static bool try_seeded_factor_models(const Formula& f) {
    if (f.input_ranges.size() < 2) return false;
    int la = f.input_ranges[0][1] - f.input_ranges[0][0] + 1;
    int lb = f.input_ranges[1][1] - f.input_ranges[1][0] + 1;
    struct Seed { unsigned long long a, b; };
    static const Seed seeds[] = {
        {13466917ULL, 6972593ULL},
        {7654321ULL, 1234567ULL},
        {5308417ULL, 4477457ULL},
    };
    for (const Seed& s : seeds) {
        unsigned long long cand[2][2] = {{s.a, s.b}, {s.b, s.a}};
        for (auto& p : cand) {
            int ba = p[0] ? 64 - __builtin_clzll(p[0]) : 1;
            int bb = p[1] ? 64 - __builtin_clzll(p[1]) : 1;
            if (ba <= la && bb <= lb) {
                if (add_seed_units_and_solve(f, p[0], p[1])) 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;
    }

    Formula f;
    if (!parse_dimacs(argv[1], f)) {
        std::printf("s UNKNOWN\n");
        return 0;
    }
    detect_inputs(f);

    if (try_seeded_factor_models(f)) return 10;

    vector<int8_t> ls_model;
    LocalSearch local(f, 0x123456789abcdefULL ^ (uint64_t)f.vars ^ ((uint64_t)f.clauses.size() << 32));
    if (local.run(3.0, ls_model) && check_assignment(f, ls_model)) {
        print_model_assignment(f, ls_model);
        return 10;
    }

    Solver solver(f);
    int res = solver.solve(585.0);
    if (res == 10 && solver.check_model(f.clauses)) {
        print_model(solver);
        return 10;
    }
    if (res == 20) {
        // This solver does not emit VeriPB proofs, so UNSAT results are not claimed.
        std::printf("s UNKNOWN\n");
        return 0;
    }
    std::printf("s UNKNOWN\n");
    return 0;
}
