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

using Clock = std::chrono::steady_clock;

struct Clause {
    std::vector<int> lits;
};

struct Edge {
    int a = 0, b = 0;
    std::vector<uint64_t> forb_ab;
};

struct CSP {
    int nvars = 0;
    int n = 0;
    int d = 0;
    std::vector<Clause> clauses;
    std::vector<std::vector<int>> blocks;
    std::vector<int> lit_to_obj;
    std::vector<int> lit_to_val;
    std::vector<Edge> edges;
    std::vector<std::vector<int>> incident;
};

static bool read_dimacs(const std::string& path, CSP& csp) {
    std::ifstream in(path);
    if (!in) return false;
    std::string tok;
    int declared_clauses = -1;
    while (in >> tok) {
        if (tok == "c") {
            std::string rest;
            std::getline(in, rest);
        } else if (tok == "p") {
            std::string fmt;
            in >> fmt >> csp.nvars >> declared_clauses;
            if (fmt != "cnf" || csp.nvars <= 0) return false;
        } else {
            int lit = std::stoi(tok);
            Clause cl;
            while (lit != 0) {
                if (lit == 0 || std::abs(lit) > csp.nvars) return false;
                cl.lits.push_back(lit);
                if (!(in >> lit)) return false;
            }
            if (cl.lits.empty()) return false;
            csp.clauses.push_back(std::move(cl));
        }
    }
    return csp.nvars > 0 && (declared_clauses < 0 || declared_clauses == (int)csp.clauses.size());
}

static bool detect_direct_encoding(CSP& csp) {
    for (const Clause& cl : csp.clauses) {
        if (cl.lits.size() > 2) csp.blocks.push_back(cl.lits);
    }
    if (csp.blocks.empty()) return false;
    std::sort(csp.blocks.begin(), csp.blocks.end(), [](const auto& a, const auto& b) {
        auto amin = std::min_element(a.begin(), a.end(), [](int x, int y) { return std::abs(x) < std::abs(y); });
        auto bmin = std::min_element(b.begin(), b.end(), [](int x, int y) { return std::abs(x) < std::abs(y); });
        return std::abs(*amin) < std::abs(*bmin);
    });
    csp.n = (int)csp.blocks.size();
    csp.d = (int)csp.blocks[0].size();
    if (csp.n < 2 || csp.d < 2 || csp.d > 63) return false;
    csp.lit_to_obj.assign(csp.nvars + 1, -1);
    csp.lit_to_val.assign(csp.nvars + 1, -1);
    std::vector<char> seen(csp.nvars + 1, 0);
    for (int i = 0; i < csp.n; ++i) {
        if ((int)csp.blocks[i].size() != csp.d) return false;
        std::sort(csp.blocks[i].begin(), csp.blocks[i].end(), [](int x, int y) {
            return std::abs(x) < std::abs(y);
        });
        for (int a = 0; a < csp.d; ++a) {
            int v = std::abs(csp.blocks[i][a]);
            if (v <= 0 || v > csp.nvars || seen[v]) return false;
            seen[v] = 1;
            csp.lit_to_obj[v] = i;
            csp.lit_to_val[v] = a;
        }
    }
    for (int v = 1; v <= csp.nvars; ++v) {
        if (!seen[v]) return false;
    }

    std::unordered_map<uint64_t, int> edge_id;
    for (const Clause& cl : csp.clauses) {
        if (cl.lits.size() != 2) continue;
        int sa = -cl.lits[0], sb = -cl.lits[1];
        int va = std::abs(sa), vb = std::abs(sb);
        int i = csp.lit_to_obj[va], j = csp.lit_to_obj[vb];
        int ai = csp.lit_to_val[va], bj = csp.lit_to_val[vb];
        if (i < 0 || j < 0) return false;
        if (csp.blocks[i][ai] != sa || csp.blocks[j][bj] != sb) return false;
        if (i == j) continue;
        if (i > j) {
            std::swap(i, j);
            std::swap(ai, bj);
        }
        uint64_t key = (uint64_t)(uint32_t)i << 32 | (uint32_t)j;
        auto it = edge_id.find(key);
        if (it == edge_id.end()) {
            int id = (int)csp.edges.size();
            edge_id.emplace(key, id);
            Edge e;
            e.a = i; e.b = j; e.forb_ab.assign(csp.d, 0);
            csp.edges.push_back(std::move(e));
            it = edge_id.find(key);
        }
        csp.edges[it->second].forb_ab[ai] |= (uint64_t(1) << bj);
    }
    csp.incident.assign(csp.n, {});
    for (int e = 0; e < (int)csp.edges.size(); ++e) {
        csp.incident[csp.edges[e].a].push_back(e);
        csp.incident[csp.edges[e].b].push_back(e);
    }
    return !csp.edges.empty();
}

static inline bool violates(const CSP&, const Edge& e, int va, int vb) {
    return ((e.forb_ab[va] >> vb) & 1ULL) != 0;
}

static inline bool edge_bad(const CSP& csp, int eid, const std::vector<int>& asn) {
    const Edge& e = csp.edges[eid];
    return violates(csp, e, asn[e.a], asn[e.b]);
}

struct LocalState {
    std::vector<int> asn;
    std::vector<int> var_conf;
    std::vector<int> var_wconf;
    std::vector<int> edge_weight;
    std::vector<int> bad_edges;
    std::vector<int> pos;
};

static void add_bad(const CSP& csp, LocalState& st, int eid) {
    if (st.pos[eid] >= 0) return;
    st.pos[eid] = (int)st.bad_edges.size();
    st.bad_edges.push_back(eid);
    st.var_conf[csp.edges[eid].a]++;
    st.var_conf[csp.edges[eid].b]++;
    st.var_wconf[csp.edges[eid].a] += st.edge_weight[eid];
    st.var_wconf[csp.edges[eid].b] += st.edge_weight[eid];
}

static void rem_bad(const CSP& csp, LocalState& st, int eid) {
    int p = st.pos[eid];
    if (p < 0) return;
    int last = st.bad_edges.back();
    st.bad_edges[p] = last;
    st.pos[last] = p;
    st.bad_edges.pop_back();
    st.pos[eid] = -1;
    st.var_conf[csp.edges[eid].a]--;
    st.var_conf[csp.edges[eid].b]--;
    st.var_wconf[csp.edges[eid].a] -= st.edge_weight[eid];
    st.var_wconf[csp.edges[eid].b] -= st.edge_weight[eid];
}

static int conflict_count_value(const CSP& csp, const LocalState& st, int var, int val, bool weighted) {
    int cnt = 0;
    for (int eid : csp.incident[var]) {
        const Edge& e = csp.edges[eid];
        int va = (e.a == var) ? val : st.asn[e.a];
        int vb = (e.b == var) ? val : st.asn[e.b];
        if (violates(csp, e, va, vb)) cnt += weighted ? st.edge_weight[eid] : 1;
    }
    return cnt;
}

static void bump_bad_weights(const CSP& csp, LocalState& st) {
    for (int eid : st.bad_edges) {
        st.edge_weight[eid]++;
        st.var_wconf[csp.edges[eid].a]++;
        st.var_wconf[csp.edges[eid].b]++;
    }
}

static void set_value(const CSP& csp, LocalState& st, int var, int val) {
    if (st.asn[var] == val) return;
    for (int eid : csp.incident[var]) {
        bool was = st.pos[eid] >= 0;
        const Edge& e = csp.edges[eid];
        int va = (e.a == var) ? val : st.asn[e.a];
        int vb = (e.b == var) ? val : st.asn[e.b];
        bool now = violates(csp, e, va, vb);
        if (was && !now) rem_bad(csp, st, eid);
        else if (!was && now) add_bad(csp, st, eid);
    }
    st.asn[var] = val;
}

static void init_greedy(const CSP& csp, LocalState& st, std::mt19937_64& rng) {
    st.asn.assign(csp.n, 0);
    std::vector<int> order(csp.n);
    std::iota(order.begin(), order.end(), 0);
    std::sort(order.begin(), order.end(), [&](int a, int b) {
        return csp.incident[a].size() > csp.incident[b].size();
    });
    std::vector<char> assigned(csp.n, 0);
    for (int var : order) {
        int best = 1000000;
        std::vector<int> vals;
        for (int val = 0; val < csp.d; ++val) {
            int cnt = 0;
            for (int eid : csp.incident[var]) {
                const Edge& e = csp.edges[eid];
                int other = e.a == var ? e.b : e.a;
                if (!assigned[other]) continue;
                int va = (e.a == var) ? val : st.asn[e.a];
                int vb = (e.b == var) ? val : st.asn[e.b];
                cnt += violates(csp, e, va, vb);
            }
            if (cnt < best) { best = cnt; vals.clear(); vals.push_back(val); }
            else if (cnt == best) vals.push_back(val);
        }
        st.asn[var] = vals[(size_t)(rng() % vals.size())];
        assigned[var] = 1;
    }
    st.var_conf.assign(csp.n, 0);
    st.var_wconf.assign(csp.n, 0);
    st.edge_weight.assign(csp.edges.size(), 1);
    st.bad_edges.clear();
    st.pos.assign(csp.edges.size(), -1);
    for (int e = 0; e < (int)csp.edges.size(); ++e) {
        if (edge_bad(csp, e, st.asn)) add_bad(csp, st, e);
    }
}

static bool local_search(const CSP& csp, std::vector<int>& solution, double seconds) {
    auto deadline = Clock::now() + std::chrono::milliseconds((int)(seconds * 1000));
    uint64_t seed = 1469598103934665603ULL ^ (uint64_t)csp.nvars * 1099511628211ULL ^ (uint64_t)csp.clauses.size();
    for (const Edge& e : csp.edges) {
        seed ^= (uint64_t)(e.a + 0x9e3779b9U) + (seed << 6) + (seed >> 2);
        seed ^= (uint64_t)(e.b + 0x85ebca6bU) + (seed << 6) + (seed >> 2);
        for (uint64_t m : e.forb_ab) {
            seed ^= m + 0x9e3779b97f4a7c15ULL + (seed << 6) + (seed >> 2);
        }
    }
    std::mt19937_64 rng(seed);
    LocalState st;
    int best_seen = 1 << 30;
    int restart = 0;
    while (Clock::now() < deadline) {
        init_greedy(csp, st, rng);
        int stagnant = 0;
        int limit = 200000 + 15000 * csp.n + restart * 1000;
        for (int step = 0; step < limit && Clock::now() < deadline; ++step) {
            if (st.bad_edges.empty()) {
                solution = st.asn;
                return true;
            }
            if ((int)st.bad_edges.size() < best_seen) {
                best_seen = (int)st.bad_edges.size();
                stagnant = 0;
            } else {
                stagnant++;
            }
            if (stagnant > 50000 + 1200 * csp.n) break;
            int best_delta = 1000000;
            std::vector<std::pair<int, int>> moves;
            int eid = st.bad_edges[(size_t)(rng() % st.bad_edges.size())];
            const Edge& e = csp.edges[eid];
            int candidates[3] = {e.a, e.b, -1};
            if ((rng() % 100) < 10) {
                int be = st.bad_edges[(size_t)(rng() % st.bad_edges.size())];
                candidates[2] = (rng() & 1) ? csp.edges[be].a : csp.edges[be].b;
            }
            for (int ci = 0; ci < 3; ++ci) {
                int var = candidates[ci];
                if (var < 0) continue;
                int cur = st.var_wconf[var];
                for (int val = 0; val < csp.d; ++val) {
                    if (val == st.asn[var]) continue;
                    int nc = conflict_count_value(csp, st, var, val, true);
                    int delta = nc - cur;
                    if (delta < best_delta) { best_delta = delta; moves.clear(); moves.emplace_back(var, val); }
                    else if (delta == best_delta) moves.emplace_back(var, val);
                }
            }
            if (best_delta >= 0 && (stagnant % 13 == 0)) bump_bad_weights(csp, st);
            int var, val;
            int noise = (int)(rng() % 1000);
            if (noise < 18) {
                int be = st.bad_edges[(size_t)(rng() % st.bad_edges.size())];
                var = (rng() & 1) ? csp.edges[be].a : csp.edges[be].b;
                val = (int)(rng() % csp.d);
            } else {
                auto mv = moves[(size_t)(rng() % moves.size())];
                var = mv.first;
                val = mv.second;
            }
            set_value(csp, st, var, val);
        }
        restart++;
    }
    std::cerr << "c local search best_conflicts " << best_seen << "\n";
    return false;
}

static bool verify_model(const CSP& csp, const std::vector<int>& sol, std::vector<int>& model) {
    model.assign(csp.nvars + 1, 0);
    for (int i = 0; i < csp.n; ++i) {
        if (sol[i] < 0 || sol[i] >= csp.d) return false;
        for (int a = 0; a < csp.d; ++a) {
            int s = csp.blocks[i][a];
            int v = std::abs(s);
            bool selected = (a == sol[i]);
            bool truth = selected ? (s > 0) : (s < 0);
            model[v] = truth ? v : -v;
        }
    }
    for (const Clause& cl : csp.clauses) {
        bool sat = false;
        for (int lit : cl.lits) {
            int v = std::abs(lit);
            int val = model[v];
            if ((lit > 0 && val > 0) || (lit < 0 && val < 0)) {
                sat = true;
                break;
            }
        }
        if (!sat) return false;
    }
    return true;
}

static void print_model(const std::vector<int>& model) {
    std::cout << "s SATISFIABLE\n";
    int count = 0;
    std::cout << "v ";
    for (int v = 1; v < (int)model.size(); ++v) {
        std::cout << model[v] << ' ';
        if (++count % 20 == 0) std::cout << "\nv ";
    }
    std::cout << "0\n";
}

int main(int argc, char** argv) {
    if (argc != 3) {
        std::fprintf(stderr, "usage: solver <formula.cnf> <out.pbp>\n");
        return 1;
    }
    CSP csp;
    if (!read_dimacs(argv[1], csp) || !detect_direct_encoding(csp)) {
        std::cout << "c unsupported formula shape\ns UNKNOWN\n";
        return 0;
    }
    std::cout << "c detected direct binary CSP n=" << csp.n << " d=" << csp.d
              << " edges=" << csp.edges.size() << "\n";
    std::vector<int> sol, model;
    if (local_search(csp, sol, 585.0) && verify_model(csp, sol, model)) {
        print_model(model);
        return 10;
    }
    std::cout << "s UNKNOWN\n";
    return 0;
}
