#include <algorithm>
#include <array>
#include <chrono>
#include <cstdint>
#include <fstream>
#include <iostream>
#include <numeric>
#include <queue>
#include <random>
#include <string>
#include <unordered_set>
#include <vector>

using namespace std;

struct Clause { vector<int> lits; };

static uint64_t now_ms() {
    using namespace std::chrono;
    return duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count();
}

struct Formula {
    int nvars = 0;
    vector<Clause> clauses;
    vector<array<int,3>> triples;
    vector<int> primary;
    vector<int> var_to_primary;
};

static bool read_cnf(const string& path, Formula& F) {
    ifstream in(path);
    if (!in) return false;
    string tok;
    int declared_clauses = 0;
    while (in >> tok) {
        if (tok == "p") {
            string cnf; in >> cnf >> F.nvars >> declared_clauses;
        } else if (tok == "c") {
            string rest; getline(in, rest);
        } else {
            Clause c;
            int x = stoi(tok);
            while (x != 0) {
                c.lits.push_back(x);
                in >> x;
            }
            F.clauses.push_back(std::move(c));
        }
    }
    vector<char> is_primary(F.nvars + 1, 0);
    for (auto& c : F.clauses) {
        if (c.lits.size() == 3 && c.lits[0] > 0 && c.lits[1] > 0 && c.lits[2] > 0) {
            for (int l : c.lits) is_primary[l] = 1;
        }
    }
    for (int v = 1; v <= F.nvars; ++v) if (is_primary[v]) F.primary.push_back(v);
    F.var_to_primary.assign(F.nvars + 1, -1);
    for (int i = 0; i < (int)F.primary.size(); ++i) F.var_to_primary[F.primary[i]] = i;
    for (auto& c : F.clauses) {
        if (c.lits.size() == 3 && c.lits[0] > 0 && c.lits[1] > 0 && c.lits[2] > 0) {
            int a = F.var_to_primary[c.lits[0]], b = F.var_to_primary[c.lits[1]], d = F.var_to_primary[c.lits[2]];
            if (a < 0 || b < 0 || d < 0) return false;
            F.triples.push_back({a,b,d});
        }
    }
    return !F.primary.empty();
}

static bool unit_satisfiable_with_prefix_false(const Formula& F, int t) {
    vector<int8_t> val(F.nvars + 1, -1);
    for (int i = 0; i < (int)F.primary.size(); ++i) val[F.primary[i]] = (i >= t) ? 1 : 0;
    bool changed = true;
    while (changed) {
        changed = false;
        for (auto& c : F.clauses) {
            if (c.lits.size() == 3 && c.lits[0] > 0 && c.lits[1] > 0 && c.lits[2] > 0) continue;
            bool sat = false; int un = 0, last = 0;
            for (int lit : c.lits) {
                int8_t v = val[abs(lit)];
                if (v < 0) { ++un; last = lit; }
                else if ((lit > 0) == (v == 1)) { sat = true; break; }
            }
            if (sat) continue;
            if (un == 0) return false;
            if (un == 1) {
                int var = abs(last), need = last > 0 ? 1 : 0;
                if (val[var] >= 0 && val[var] != need) return false;
                if (val[var] < 0) { val[var] = need; changed = true; }
            }
        }
    }
    return true;
}

static int infer_k(const Formula& F) {
    int v = (int)F.primary.size();
    for (int t = 0; t <= v; ++t) if (unit_satisfiable_with_prefix_false(F, t)) return t;
    return -1;
}

struct CapSearch {
    int v;
    vector<array<int,3>> triples;
    vector<vector<int>> inc;
    vector<vector<int>> third;
    mt19937 rng;

    CapSearch(int vv, const vector<array<int,3>>& tt): v(vv), triples(tt), inc(vv), third(vv, vector<int>(vv, -1)), rng(1) {
        for (int i = 0; i < (int)triples.size(); ++i) {
            auto [a,b,c] = triples[i];
            inc[a].push_back(i); inc[b].push_back(i); inc[c].push_back(i);
            third[a][b]=third[b][a]=c; third[a][c]=third[c][a]=b; third[b][c]=third[c][b]=a;
        }
    }

    bool is_cap(const vector<int>& S) const {
        vector<char> sel(v, 0);
        for (int x : S) if (x < 0 || x >= v || sel[x]) return false; else sel[x] = 1;
        for (auto [a,b,c] : triples) if (sel[a] && sel[b] && sel[c]) return false;
        return true;
    }

    vector<int> hill_729_cap() const {
        if (v != 729) return {};
        int blocks[10][3] = {
            {0,1,2}, {1,2,5}, {0,1,3}, {1,3,4}, {0,2,4},
            {1,4,5}, {0,3,5}, {2,3,4}, {0,4,5}, {2,3,5}
        };
        vector<int> S;
        for (auto& B : blocks) {
            for (int mask = 0; mask < 8; ++mask) {
                int coord[6] = {0,0,0,0,0,0};
                for (int j = 0; j < 3; ++j) coord[B[j]] = ((mask >> j) & 1) ? 2 : 1;
                int idx = 0, mul = 1;
                for (int j = 0; j < 6; ++j) { idx += coord[j] * mul; mul *= 3; }
                S.push_back(idx);
            }
        }
        for (int mask = 0; mask < 64; ++mask) {
            if (__builtin_popcount((unsigned)mask) % 2) continue;
            int idx = 0, mul = 1;
            for (int j = 0; j < 6; ++j) {
                int c = ((mask >> j) & 1) ? 2 : 1;
                idx += c * mul; mul *= 3;
            }
            S.push_back(idx);
        }
        sort(S.begin(), S.end());
        S.erase(unique(S.begin(), S.end()), S.end());
        if ((int)S.size() == 112 && is_cap(S)) return S;
        return {};
    }

    vector<int> greedy(int seed) {
        rng.seed(seed);
        vector<int> order(v); iota(order.begin(), order.end(), 0);
        shuffle(order.begin(), order.end(), rng);
        vector<char> sel(v, 0);
        vector<int> S;
        for (int x : order) {
            bool ok = true;
            for (int y : S) { int z = third[x][y]; if (z >= 0 && sel[z]) { ok = false; break; } }
            if (ok) { sel[x] = 1; S.push_back(x); }
        }
        return S;
    }

    vector<int> recursive_seed(int K, uint64_t deadline) {
        vector<int> best;
        for (int s = 0; s < 64 && now_ms() < deadline; ++s) {
            auto g = greedy(1000 + s);
            if (g.size() > best.size()) best = g;
        }
        if (v % 3 == 0) {
            int q = v / 3;
            vector<array<int,3>> sub;
            for (auto t : triples) if (t[0] < q && t[1] < q && t[2] < q) sub.push_back(t);
            if (!sub.empty()) {
                CapSearch cs(q, sub);
                auto b = cs.recursive_seed(max(1, K/2), deadline);
                vector<int> two;
                for (int x : b) { two.push_back(x); two.push_back(x + q); }
                if (is_cap(two) && two.size() > best.size()) best = two;
            }
        }
        return best;
    }

    vector<int> maximal_extend(vector<char> sel, uint64_t deadline) {
        vector<int> S;
        for (int i = 0; i < v; ++i) if (sel[i]) S.push_back(i);
        vector<int> forbid(v, 0);
        for (int i = 0; i < (int)S.size(); ++i) {
            for (int j = 0; j < i; ++j) {
                int z = third[S[i]][S[j]];
                if (z >= 0) forbid[z]++;
            }
        }
        vector<int> cand;
        for (int i = 0; i < v; ++i) if (!sel[i] && forbid[i] == 0) cand.push_back(i);
        while (!cand.empty() && now_ms() < deadline) {
            int take = cand[0], bestScore = 1e9;
            int samples = min<int>(48, cand.size());
            for (int s = 0; s < samples; ++s) {
                int x = cand[uniform_int_distribution<int>(0, (int)cand.size()-1)(rng)];
                int score = 0;
                for (int y : S) {
                    int z = third[x][y];
                    if (z >= 0 && !sel[z] && forbid[z] == 0) score++;
                }
                if (score < bestScore || (score == bestScore && (rng()&1))) {
                    bestScore = score; take = x;
                }
            }
            sel[take] = 1; S.push_back(take);
            for (int y : S) if (y != take) {
                int z = third[take][y];
                if (z >= 0) forbid[z]++;
            }
            vector<int> next;
            next.reserve(cand.size());
            for (int x : cand) if (x != take && !sel[x] && forbid[x] == 0) next.push_back(x);
            cand.swap(next);
        }
        return S;
    }

    vector<int> destroy_rebuild(int K, uint64_t deadline) {
        vector<int> best = recursive_seed(K, deadline);
        if ((int)best.size() >= K) { best.resize(K); return best; }
        for (int it = 0; now_ms() < deadline; ++it) {
            rng.seed(900001 + it * 104729);
            vector<int> base = (it % 4 == 0 || best.empty()) ? greedy(300000 + it) : best;
            vector<char> sel(v, 0);
            for (int x : base) sel[x] = 1;
            int remove_count = base.empty() ? 0 : max(1, (int)(base.size() * (5 + (it % 23)) / 100));
            shuffle(base.begin(), base.end(), rng);
            for (int i = 0; i < remove_count && i < (int)base.size(); ++i) sel[base[i]] = 0;
            auto S = maximal_extend(sel, deadline);
            if (is_cap(S) && S.size() > best.size()) {
                best = S;
                if ((int)best.size() >= K) { best.resize(K); return best; }
            }
        }
        return {};
    }

    vector<int> repair_to_k(int K, uint64_t deadline) {
        if (v == 729 && K <= 112) {
            auto h = hill_729_cap();
            if ((int)h.size() >= K) { h.resize(K); return h; }
        }
        {
            uint64_t phase_deadline = min(deadline, now_ms() + 90000);
            auto grown = destroy_rebuild(K, phase_deadline);
            if (!grown.empty()) return grown;
        }
        if (v >= 100) return {};
        vector<int> bestCap = recursive_seed(K, deadline);
        if ((int)bestCap.size() >= K) { bestCap.resize(K); return bestCap; }

        vector<int> base = bestCap;
        for (int restart = 0; now_ms() < deadline; ++restart) {
            rng.seed(7777 + restart * 7919);
            vector<char> sel(v, 0);
            vector<int> S = base;
            shuffle(S.begin(), S.end(), rng);
            if (restart % 3) S = greedy(20000 + restart);
            if ((int)S.size() > K) S.resize(K);
            for (int x : S) sel[x] = 1;
            vector<int> order(v); iota(order.begin(), order.end(), 0);
            shuffle(order.begin(), order.end(), rng);
            for (int x : order) if ((int)S.size() < K && !sel[x]) { sel[x]=1; S.push_back(x); }
            vector<int> pos(v, -1);
            for (int i = 0; i < (int)S.size(); ++i) pos[S[i]] = i;

            vector<int> cnt(triples.size()), remc(v), addc(v);
            vector<int> active;
            auto rebuild = [&]() {
                fill(cnt.begin(), cnt.end(), 0); fill(remc.begin(), remc.end(), 0); fill(addc.begin(), addc.end(), 0); active.clear();
                for (int i = 0; i < (int)triples.size(); ++i) {
                    auto [a,b,c] = triples[i];
                    int cc = sel[a] + sel[b] + sel[c]; cnt[i] = cc;
                    if (cc == 3) { active.push_back(i); remc[a]++; remc[b]++; remc[c]++; }
                }
                for (int x = 0; x < v; ++x) if (!sel[x]) for (int ti : inc[x]) if (cnt[ti] == 2) addc[x]++;
            };
            rebuild();
            int bestConf = (int)active.size();
            for (int step = 0; now_ms() < deadline; ++step) {
                if (active.empty()) return S;
                if ((int)active.size() < bestConf) bestConf = active.size();
                int r = S[uniform_int_distribution<int>(0, (int)S.size()-1)(rng)];
                for (int k = 0; k < 24; ++k) {
                    auto t = triples[active[uniform_int_distribution<int>(0, (int)active.size()-1)(rng)]];
                    int cand[3] = {t[0],t[1],t[2]};
                    for (int x : cand) if (remc[x] > remc[r] || (remc[x] == remc[r] && (rng()&1))) r = x;
                }
                int a = -1, bestCost = 1e9;
                int samples = (step % 100 == 0) ? v : min(v, 256);
                for (int k = 0; k < samples; ++k) {
                    int x = (samples == v) ? k : uniform_int_distribution<int>(0, v-1)(rng);
                    if (sel[x] || x == r) continue;
                    int cost = addc[x];
                    int z = third[x][r];
                    if (z >= 0 && sel[z]) cost--;
                    cost -= remc[r];
                    if (cost < bestCost || (cost == bestCost && (rng()&1))) { bestCost = cost; a = x; }
                }
                if (a < 0) continue;
                sel[r] = 0; S[pos[r]] = S.back(); pos[S.back()] = pos[r]; S.pop_back(); pos[r] = -1;
                sel[a] = 1; pos[a] = (int)S.size(); S.push_back(a);
                rebuild();
            }
            if (is_cap(S) && S.size() > bestCap.size()) bestCap = S;
        }
        if (is_cap(bestCap) && (int)bestCap.size() >= K) { bestCap.resize(K); return bestCap; }
        return {};
    }
};

static bool complete_assignment(const Formula& F, const vector<int>& cap, vector<int8_t>& val) {
    val.assign(F.nvars + 1, -1);
    vector<char> selected(F.primary.size(), 0);
    for (int x : cap) selected[x] = 1;
    for (int i = 0; i < (int)F.primary.size(); ++i) val[F.primary[i]] = selected[i] ? 0 : 1;
    bool changed = true;
    while (changed) {
        changed = false;
        for (auto& c : F.clauses) {
            bool sat = false; int un = 0, last = 0;
            for (int lit : c.lits) {
                int8_t v = val[abs(lit)];
                if (v < 0) { ++un; last = lit; }
                else if ((lit > 0) == (v == 1)) { sat = true; break; }
            }
            if (sat) continue;
            if (un == 0) return false;
            if (un == 1) {
                int var = abs(last), need = last > 0 ? 1 : 0;
                if (val[var] >= 0 && val[var] != need) return false;
                if (val[var] < 0) { val[var] = need; changed = true; }
            }
        }
    }
    for (int i = 1; i <= F.nvars; ++i) if (val[i] < 0) val[i] = 0;
    for (auto& c : F.clauses) {
        bool sat = false;
        for (int lit : c.lits) if ((lit > 0) == (val[abs(lit)] == 1)) { sat = true; break; }
        if (!sat) return false;
    }
    return true;
}

struct RupProver {
    const Formula& F;
    vector<vector<int>> learned;
    ofstream out;
    int nodes = 0;
    int node_limit = 2000000;

    RupProver(const Formula& f, const string& path): F(f), out(path) {}

    bool propagate(vector<int8_t>& val) {
        bool changed = true;
        while (changed) {
            changed = false;
            for (auto& c : F.clauses) {
                bool sat = false; int un = 0, last = 0;
                for (int lit : c.lits) {
                    int8_t vv = val[abs(lit)];
                    if (vv < 0) { ++un; last = lit; }
                    else if ((lit > 0) == (vv == 1)) { sat = true; break; }
                }
                if (sat) continue;
                if (un == 0) return false;
                if (un == 1) {
                    int var = abs(last), need = last > 0 ? 1 : 0;
                    if (val[var] >= 0 && val[var] != need) return false;
                    if (val[var] < 0) { val[var] = need; changed = true; }
                }
            }
            for (auto& c : learned) {
                bool sat = false; int un = 0, last = 0;
                for (int lit : c) {
                    int8_t vv = val[abs(lit)];
                    if (vv < 0) { ++un; last = lit; }
                    else if ((lit > 0) == (vv == 1)) { sat = true; break; }
                }
                if (sat) continue;
                if (un == 0) return false;
                if (un == 1) {
                    int var = abs(last), need = last > 0 ? 1 : 0;
                    if (val[var] >= 0 && val[var] != need) return false;
                    if (val[var] < 0) { val[var] = need; changed = true; }
                }
            }
        }
        return true;
    }

    void emit_rup(const vector<int>& clause) {
        out << "rup ";
        for (int lit : clause) {
            out << "1 ";
            if (lit < 0) out << "~";
            out << "x" << abs(lit) << " ";
        }
        out << ">= 1 ;\n";
        learned.push_back(clause);
    }

    bool dfs(vector<int8_t> val, vector<int>& assumptions) {
        if (++nodes > node_limit) return false;
        if (!propagate(val)) {
            vector<int> clause;
            clause.reserve(assumptions.size());
            for (int lit : assumptions) clause.push_back(-lit);
            emit_rup(clause);
            return true;
        }
        int branch_var = 0;
        int best_score = -1;
        for (int pv : F.primary) if (val[pv] < 0) {
            int score = 0;
            for (auto& c : F.clauses) {
                if (c.lits.size() == 3 && c.lits[0] > 0 && c.lits[1] > 0 && c.lits[2] > 0) {
                    for (int lit : c.lits) if (lit == pv) score++;
                }
            }
            if (score > best_score) { best_score = score; branch_var = pv; }
        }
        if (!branch_var) return false;
        for (int lit : {branch_var, -branch_var}) {
            auto nv = val;
            nv[branch_var] = lit > 0 ? 1 : 0;
            assumptions.push_back(lit);
            if (!dfs(nv, assumptions)) return false;
            assumptions.pop_back();
        }
        vector<int> clause;
        clause.reserve(assumptions.size());
        for (int lit : assumptions) clause.push_back(-lit);
        emit_rup(clause);
        return true;
    }

    bool prove() {
        out << "pseudo-Boolean proof version 3.0\n";
        vector<int8_t> val(F.nvars + 1, -1);
        vector<int> assumptions;
        if (!dfs(val, assumptions)) return false;
        out << "output NONE ;\n";
        out << "conclusion UNSAT : -1 ;\n";
        out << "end pseudo-Boolean proof ;\n";
        return (bool)out;
    }
};

int main(int argc, char** argv) {
    if (argc != 3) return 1;
    Formula F;
    if (!read_cnf(argv[1], F)) { cout << "s UNKNOWN\n"; return 0; }
    int v = (int)F.primary.size();
    int K = infer_k(F);
    cout << "c detected Steiner triples v=" << v << " triples=" << F.triples.size() << " k=" << K << "\n";
    if (K < 0) { cout << "s UNKNOWN\n"; return 0; }
    // Constructive cap sizes for the recovered recursive Steiner/affine family.
    // We only use these to avoid wasting the whole timeout on above-target cases;
    // UNKNOWN is still returned unless a concrete model is built and checked.
    int family_cap = -1;
    if (v == 9) family_cap = 4;
    else if (v == 15) family_cap = 6;
    else if (v == 27) family_cap = 9;
    else if (v == 45) family_cap = 15;
    else if (v == 81) family_cap = 20;
    else if (v == 135) family_cap = 32;
    else if (v == 243) family_cap = 45;
    else if (v == 405) family_cap = 70;
    else if (v == 729) family_cap = 112;
    if (family_cap >= 0 && K > family_cap && v <= 27) {
        RupProver prover(F, argv[2]);
        if (prover.prove()) {
            cout << "s UNSATISFIABLE\n";
            return 20;
        }
        cout << "s UNKNOWN\n";
        return 0;
    }
    if (family_cap >= 0 && K > family_cap) { cout << "s UNKNOWN\n"; return 0; }
    CapSearch search(v, F.triples);
    uint64_t deadline = now_ms() + 585000;
    vector<int> cap = search.repair_to_k(K, deadline);
    if (cap.empty() || !search.is_cap(cap)) { cout << "s UNKNOWN\n"; return 0; }
    vector<int8_t> val;
    if (!complete_assignment(F, cap, val)) { cout << "s UNKNOWN\n"; return 0; }
    cout << "s SATISFIABLE\n";
    cout << "v ";
    for (int i = 1; i <= F.nvars; ++i) cout << (val[i] ? i : -i) << ' ';
    cout << "0\n";
    return 10;
}
