#include <algorithm>
#include <chrono>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <numeric>
#include <string>
#include <vector>

using namespace std;

struct Clause {
    vector<int> lits;
};

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

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

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

static inline bool lit_true(int lit, const vector<unsigned char>& val) {
    int v = abs(lit) - 1;
    int block = v >> 2;
    int color = v & 3;
    bool selected = val[block] == color;
    return lit > 0 ? selected : !selected;
}

static bool clause_sat(const Clause& c, const vector<unsigned char>& val) {
    for (int lit : c.lits) {
        if (lit_true(lit, val)) return true;
    }
    return false;
}

static bool all_sat(const Formula& f, const vector<unsigned char>& val) {
    for (const Clause& c : f.clauses) {
        if (!clause_sat(c, val)) return false;
    }
    return true;
}

static bool detect_four_blocks(const Formula& f) {
    if (f.vars % 4 != 0) return false;
    int blocks = f.vars / 4;
    int alo = 0, amo = 0, other_ok = 0;
    for (const Clause& c : f.clauses) {
        if (c.lits.size() == 4) {
            vector<int> xs = c.lits;
            sort(xs.begin(), xs.end());
            for (int b = 0; b < blocks; ++b) {
                int a = 4 * b + 1;
                if (xs[0] == a && xs[1] == a + 1 && xs[2] == a + 2 && xs[3] == a + 3) {
                    ++alo;
                    break;
                }
            }
            ++other_ok;
        } else if (c.lits.size() == 2 && c.lits[0] < 0 && c.lits[1] < 0) {
            int a = -c.lits[0] - 1, b = -c.lits[1] - 1;
            if ((a >> 2) == (b >> 2) && (a & 3) != (b & 3)) ++amo;
            ++other_ok;
        } else if (c.lits.size() >= 1 && c.lits.size() <= 4) {
            bool in_blocks = true;
            for (int lit : c.lits) {
                int v = abs(lit);
                if (v < 1 || v > f.vars) in_blocks = false;
            }
            if (in_blocks) ++other_ok;
        }
    }
    return alo >= blocks * 9 / 10 && amo >= blocks * 5 && other_ok == (int)f.clauses.size();
}

static bool solve_block_sls(const Formula& f, vector<unsigned char>& val, double seconds) {
    const int blocks = f.vars / 4;
    vector<vector<int>> occ(blocks);
    for (int ci = 0; ci < (int)f.clauses.size(); ++ci) {
        int last = -1;
        vector<int> bs;
        for (int lit : f.clauses[ci].lits) {
            int b = (abs(lit) - 1) >> 2;
            if (b != last) bs.push_back(b);
            last = b;
        }
        sort(bs.begin(), bs.end());
        bs.erase(unique(bs.begin(), bs.end()), bs.end());
        for (int b : bs) occ[b].push_back(ci);
    }

    vector<unsigned char> sat(f.clauses.size(), 0);
    vector<int> unsat, pos(f.clauses.size(), -1);
    auto set_unsat = [&](int ci, bool is_unsat) {
        if (is_unsat) {
            if (pos[ci] < 0) {
                pos[ci] = (int)unsat.size();
                unsat.push_back(ci);
            }
        } else if (pos[ci] >= 0) {
            int p = pos[ci], last = unsat.back();
            unsat[p] = last;
            pos[last] = p;
            unsat.pop_back();
            pos[ci] = -1;
        }
    };
    auto initialize_state = [&]() {
        unsat.clear();
        fill(pos.begin(), pos.end(), -1);
        for (int ci = 0; ci < (int)f.clauses.size(); ++ci) {
            sat[ci] = clause_sat(f.clauses[ci], val);
            if (!sat[ci]) {
                pos[ci] = (int)unsat.size();
                unsat.push_back(ci);
            }
        }
    };

    auto start = chrono::steady_clock::now();
    auto elapsed = [&]() {
        return chrono::duration<double>(chrono::steady_clock::now() - start).count();
    };

    uint64_t seed_base = 0x4d595df4d0f33173ULL ^ (uint64_t)f.vars * 1000003ULL ^
                         (uint64_t)f.clauses.size() * 9176ULL;
    int restart = 0;
    long long total_steps = 0;
    while (elapsed() < seconds) {
        XorShift64 rng(seed_base + 0x9e3779b97f4a7c15ULL * (uint64_t)(restart + 1));
        val.assign(blocks, 0);
        for (int b = 0; b < blocks; ++b) val[b] = (unsigned char)rng.randint(4);

        // Satisfy unit clauses in the initial point when possible.
        for (const Clause& c : f.clauses) {
            if (c.lits.size() == 1) {
                int lit = c.lits[0], b = (abs(lit) - 1) >> 2, color = (abs(lit) - 1) & 3;
                if (lit > 0) val[b] = (unsigned char)color;
                else if (val[b] == color) val[b] = (unsigned char)((color + 1 + rng.randint(3)) & 3);
            }
        }
        initialize_state();
        if (unsat.empty()) return true;

        int max_steps = max(20000, blocks * 2500);
        for (int step = 0; step < max_steps && elapsed() < seconds; ++step, ++total_steps) {
            if (unsat.empty()) return true;
            int ci = unsat[rng.randint((int)unsat.size())];
            const Clause& c = f.clauses[ci];
            int lit = c.lits[rng.randint((int)c.lits.size())];
            int b = (abs(lit) - 1) >> 2;
            int old = val[b];

            int best_delta = 1000000000;
            int best_colors[4];
            int best_n = 0;
            for (int color = 0; color < 4; ++color) {
                if (color == old) continue;
                int delta = 0;
                val[b] = (unsigned char)color;
                for (int oc : occ[b]) {
                    bool ns = clause_sat(f.clauses[oc], val);
                    if ((bool)sat[oc] != ns) delta += ns ? -1 : 1;
                }
                if (delta < best_delta) {
                    best_delta = delta;
                    best_n = 0;
                    best_colors[best_n++] = color;
                } else if (delta == best_delta) {
                    best_colors[best_n++] = color;
                }
            }
            val[b] = (unsigned char)old;
            int chosen;
            if (best_delta <= 0 || (rng.next() & 1023ULL) == 0) {
                chosen = best_colors[rng.randint(best_n)];
            } else {
                chosen = rng.randint(4);
                if (chosen == old) chosen = (chosen + 1) & 3;
            }
            if (chosen == old) continue;
            val[b] = (unsigned char)chosen;
            for (int oc : occ[b]) {
                bool ns = clause_sat(f.clauses[oc], val);
                if ((bool)sat[oc] != ns) {
                    sat[oc] = (unsigned char)ns;
                    set_unsat(oc, !ns);
                }
            }
        }
        ++restart;
    }
    fprintf(stderr, "c sls exhausted restarts=%d steps=%lld remaining=%zu\n", restart, total_steps, unsat.size());
    return false;
}

static void print_model(const Formula& f, const vector<unsigned char>& val) {
    printf("s SATISFIABLE\n");
    const int blocks = f.vars / 4;
    printf("v");
    int on_line = 0;
    for (int b = 0; b < blocks; ++b) {
        for (int c = 0; c < 4; ++c) {
            int var = 4 * b + c + 1;
            int lit = (val[b] == c) ? var : -var;
            printf(" %d", lit);
            if (++on_line == 20) {
                printf("\nv");
                on_line = 0;
            }
        }
    }
    printf(" 0\n");
}

int main(int argc, char** argv) {
    if (argc != 3) {
        fprintf(stderr, "usage: solver <formula.cnf> <out.pbp>\n");
        return 1;
    }
    Formula f;
    if (!read_dimacs(argv[1], f)) {
        printf("c could not parse DIMACS\ns UNKNOWN\n");
        return 0;
    }
    if (!detect_four_blocks(f)) {
        printf("c unsupported structure: vars=%d clauses=%zu\ns UNKNOWN\n", f.vars, f.clauses.size());
        return 0;
    }
    printf("c detected four-valued block CSP with %d blocks and %zu clauses\n", f.vars / 4, f.clauses.size());
    vector<unsigned char> val;
    if (!solve_block_sls(f, val, 25.0)) {
        printf("s UNKNOWN\n");
        return 0;
    }
    if (!all_sat(f, val)) {
        fprintf(stderr, "c internal error: candidate failed full clause check\n");
        printf("s UNKNOWN\n");
        return 0;
    }
    print_model(f, val);
    return 10;
}
