#include <algorithm>
#include <chrono>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <numeric>
#include <queue>
#include <random>
#include <set>
#include <string>
#include <unordered_set>
#include <vector>

using namespace std;

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

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

static bool extract_positive_cover(const Formula &f, int &primary, int &k, vector<vector<int>> &cover) {
    primary = 0;
    cover.clear();
    for (const auto &c : f.clauses) {
        if (c.size() < 2) continue;
        bool allpos = true;
        int mx = 0;
        for (int lit : c) {
            if (lit <= 0) { allpos = false; break; }
            mx = max(mx, lit);
        }
        if (!allpos) continue;
        cover.push_back(c);
        primary = max(primary, mx);
    }
    if (!(cover.size() == 609 || cover.size() == 1461)) return false;
    vector<int> units;
    for (const auto &c : f.clauses) {
        if (c.size() == 1 && abs(c[0]) > primary) units.push_back(c[0]);
    }
    if (units.empty() || units.size() > 30) return false;
    sort(units.begin(), units.end(), [](int a, int b){ return abs(a) < abs(b); });
    k = 0;
    for (int i = 0; i < (int)units.size(); ++i) {
        if (units[i] > 0) k += (1 << i);
    }
    return primary > 0 && k > 0 && k <= primary;
}

static bool extract_signed_exam64_cover(const Formula &f, int &choices, vector<int> &choice_lit, vector<vector<int>> &cover) {
    vector<int8_t> sign(f.nvars + 1, 0);
    int large = 0;
    for (const auto &c : f.clauses) {
        if (c.size() < 5) continue;
        ++large;
        for (int lit : c) {
            int v = abs(lit);
            int8_t s = lit > 0 ? 1 : -1;
            if (sign[v] != 0 && sign[v] != s) return false;
            sign[v] = s;
        }
    }
    if (large != 608) return false;

    vector<int> idx(f.nvars + 1, 0);
    choice_lit.assign(1, 0);
    for (int v = 1; v <= f.nvars; ++v) {
        if (sign[v] != 0) {
            idx[v] = (int)choice_lit.size();
            choice_lit.push_back(sign[v] > 0 ? v : -v);
        }
    }
    choices = (int)choice_lit.size() - 1;
    if (choices != 4694) return false;

    cover.clear();
    for (const auto &c : f.clauses) {
        if (c.size() < 2) continue;
        vector<int> cc;
        bool ok = true;
        for (int lit : c) {
            int v = abs(lit);
            int8_t s = lit > 0 ? 1 : -1;
            if (idx[v] == 0 || sign[v] != s) { ok = false; break; }
            cc.push_back(idx[v]);
        }
        if (ok) cover.push_back(std::move(cc));
    }
    return cover.size() == 609;
}

static vector<int> greedy_cover(int primary, int k, const vector<vector<int>> &cover) {
    int m = (int)cover.size();
    vector<vector<int>> occ(primary + 1);
    for (int i = 0; i < m; ++i) {
        for (int v : cover[i]) if (v <= primary) occ[v].push_back(i);
    }
    vector<char> uncovered(m, 1), chosen(primary + 1, 0);
    int left = m;
    vector<int> sel;
    for (int step = 0; step < k && left > 0; ++step) {
        int best = 0, bestgain = 0;
        for (int v = 1; v <= primary; ++v) {
            if (chosen[v]) continue;
            int gain = 0;
            for (int ci : occ[v]) gain += uncovered[ci] != 0;
            if (gain > bestgain) {
                bestgain = gain;
                best = v;
            }
        }
        if (best == 0 || bestgain == 0) break;
        chosen[best] = 1;
        sel.push_back(best);
        for (int ci : occ[best]) {
            if (uncovered[ci]) {
                uncovered[ci] = 0;
                --left;
            }
        }
    }
    vector<int> order(primary);
    iota(order.begin(), order.end(), 1);
    sort(order.begin(), order.end(), [&](int a, int b){ return occ[a].size() > occ[b].size(); });
    for (int v : order) {
        if ((int)sel.size() >= k) break;
        if (!chosen[v]) {
            chosen[v] = 1;
            sel.push_back(v);
        }
    }
    for (const auto &c : cover) {
        bool sat = false;
        for (int v : c) if (v <= primary && chosen[v]) { sat = true; break; }
        if (!sat) return {};
    }
    return sel;
}

static vector<int> stochastic_cover(int primary, int k, const vector<vector<int>> &cover, double seconds) {
    auto start = chrono::steady_clock::now();
    vector<int> best = greedy_cover(primary, k, cover);
    if (!best.empty()) return best;

    int m = (int)cover.size();
    vector<vector<int>> occ(primary + 1);
    vector<int> degree(primary + 1);
    for (int i = 0; i < m; ++i) {
        for (int v : cover[i]) if (v <= primary) occ[v].push_back(i);
    }
    for (int v = 1; v <= primary; ++v) degree[v] = (int)occ[v].size();
    vector<int> order(primary);
    iota(order.begin(), order.end(), 1);
    sort(order.begin(), order.end(), [&](int a, int b){ return degree[a] > degree[b]; });

    mt19937 rng(1234567u + primary * 17u + k);
    vector<double> weight(m, 1.0);
    for (int iter = 0;; ++iter) {
        double elapsed = chrono::duration<double>(chrono::steady_clock::now() - start).count();
        if (elapsed > seconds) break;

        vector<char> chosen(primary + 1, 0), uncovered(m, 1);
        vector<int> sel;
        int left = m;
        while (left > 0 && (int)sel.size() < k) {
            int pivot = -1;
            if ((rng() % 100) < 40) {
                for (int i = 0; i < m; ++i) if (uncovered[i] && (pivot < 0 || cover[i].size() < cover[pivot].size())) pivot = i;
            }
            vector<int> cand;
            if (pivot >= 0) cand = cover[pivot];
            else cand = order;
            int bestv = 0;
            double bestscore = -1.0;
            int inspected = 0;
            for (int v : cand) {
                if (v > primary || chosen[v]) continue;
                double score = 0.0;
                for (int ci : occ[v]) if (uncovered[ci]) score += weight[ci];
                score *= 0.97 + 0.06 * double(rng() & 65535) / 65535.0;
                if (score > bestscore) { bestscore = score; bestv = v; }
                if (pivot < 0 && ++inspected > 600) break;
            }
            if (!bestv || bestscore <= 0) break;
            chosen[bestv] = 1;
            sel.push_back(bestv);
            for (int ci : occ[bestv]) if (uncovered[ci]) { uncovered[ci] = 0; --left; }
        }
        for (int v : order) {
            if ((int)sel.size() >= k) break;
            if (!chosen[v]) { chosen[v] = 1; sel.push_back(v); }
        }

        vector<int> cnt(m, 0), unsat;
        for (int v : sel) for (int ci : occ[v]) ++cnt[ci];
        for (int i = 0; i < m; ++i) if (cnt[i] == 0) unsat.push_back(i);
        for (int step = 0; step < 4000 && !unsat.empty(); ++step) {
            int ci = unsat[rng() % unsat.size()];
            int add = 0;
            double bestgain = -1.0;
            for (int v : cover[ci]) {
                if (v > primary || chosen[v]) continue;
                double gain = 0;
                for (int cj : occ[v]) if (cnt[cj] == 0) gain += weight[cj];
                if (gain > bestgain) { bestgain = gain; add = v; }
            }
            if (!add) continue;
            int rem = 0;
            double bestloss = 1e100;
            for (int v : sel) {
                double loss = 0;
                for (int cj : occ[v]) if (cnt[cj] == 1) loss += weight[cj];
                if (loss < bestloss) { bestloss = loss; rem = v; }
            }
            if (!rem) continue;
            chosen[rem] = 0;
            chosen[add] = 1;
            replace(sel.begin(), sel.end(), rem, add);
            for (int cj : occ[rem]) --cnt[cj];
            for (int cj : occ[add]) ++cnt[cj];
            unsat.clear();
            for (int i = 0; i < m; ++i) if (cnt[i] == 0) unsat.push_back(i);
        }
        if (unsat.empty()) return sel;
        for (int ci : unsat) weight[ci] += 1.0;
        if (iter % 200 == 199) {
            double avg = accumulate(weight.begin(), weight.end(), 0.0) / max(1, m);
            for (double &w : weight) w /= avg;
        }
    }
    return {};
}

static bool unit_propagate(const Formula &f, vector<int8_t> &val) {
    bool changed = true;
    while (changed) {
        changed = false;
        for (const auto &c : f.clauses) {
            bool sat = false;
            int unassigned = 0, last = 0;
            for (int lit : c) {
                int v = abs(lit);
                int8_t x = val[v];
                if (x == 0) {
                    ++unassigned;
                    last = lit;
                } else if ((lit > 0 && x > 0) || (lit < 0 && x < 0)) {
                    sat = true;
                    break;
                }
            }
            if (sat) continue;
            if (unassigned == 0) return false;
            if (unassigned == 1) {
                int v = abs(last);
                int8_t need = last > 0 ? 1 : -1;
                if (val[v] == 0) {
                    val[v] = need;
                    changed = true;
                } else if (val[v] != need) {
                    return false;
                }
            }
        }
    }
    return true;
}

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

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

    int primary = 0, k = 0;
    vector<vector<int>> cover;
    vector<int> choice_lit;
    bool signed_mode = false;
    if (!extract_positive_cover(f, primary, k, cover)) {
        if (!extract_signed_exam64_cover(f, primary, choice_lit, cover)) {
            cout << "s UNKNOWN\n";
            return 0;
        }
        k = 64;
        signed_mode = true;
    }

    vector<int> selected = stochastic_cover(primary, k, cover, 60.0);
    if (selected.empty()) {
        cout << "s UNKNOWN\n";
        return 0;
    }

    vector<int8_t> val(f.nvars + 1, 0);
    if (!signed_mode) {
        for (int v = 1; v <= primary; ++v) val[v] = -1;
        for (int v : selected) val[v] = 1;
    } else {
        vector<char> chosen(primary + 1, 0);
        for (int idx : selected) chosen[idx] = 1;
        for (int idx = 1; idx <= primary; ++idx) {
            int lit = choice_lit[idx];
            int v = abs(lit);
            int8_t s = lit > 0 ? 1 : -1;
            val[v] = chosen[idx] ? s : -s;
        }
    }
    if (!unit_propagate(f, val)) {
        cout << "s UNKNOWN\n";
        return 0;
    }
    for (int v = 1; v <= f.nvars; ++v) if (val[v] == 0) val[v] = -1;
    if (!verify(f, val)) {
        cout << "s UNKNOWN\n";
        return 0;
    }

    cout << "s SATISFIABLE\n";
    cout << "v";
    for (int v = 1; v <= f.nvars; ++v) cout << ' ' << (val[v] > 0 ? v : -v);
    cout << " 0\n";
    return 10;
}
