#include <algorithm>
#include <chrono>
#include <climits>
#include <cctype>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <map>
#include <numeric>
#include <random>
#include <set>
#include <string>
#include <utility>
#include <vector>

using namespace std;

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

static CNF read_cnf(const string& path) {
    FILE* f = fopen(path.c_str(), "r");
    if (!f) {
        perror(path.c_str());
        exit(1);
    }
    CNF cnf;
    char buf[1 << 20];
    vector<int> cur;
    while (fgets(buf, sizeof(buf), f)) {
        if (buf[0] == 'c' || buf[0] == '\n') continue;
        if (buf[0] == 'p') {
            char tmp[32];
            if (sscanf(buf, "p %31s %d", tmp, &cnf.nvars) != 2) exit(1);
            continue;
        }
        char* p = buf;
        while (*p) {
            while (*p && isspace((unsigned char)*p)) ++p;
            if (!*p) break;
            char* e = nullptr;
            long x = strtol(p, &e, 10);
            p = e;
            if (x == 0) {
                if (!cur.empty()) {
                    cnf.clauses.push_back(cur);
                    cur.clear();
                }
            } else {
                cur.push_back((int)x);
            }
        }
    }
    if (!cur.empty()) cnf.clauses.push_back(cur);
    fclose(f);
    return cnf;
}

static long long comb3(long long x) {
    return x * (x - 1) * (x - 2) / 6;
}

static bool gate3(const vector<vector<int>>& c, int i, int& a, int& b, int& d) {
    if (i + 2 >= (int)c.size()) return false;
    const auto& x = c[i];
    const auto& y = c[i + 1];
    const auto& z = c[i + 2];
    if (x.size() != 3 || y.size() != 3 || z.size() != 3) return false;
    a = -x[0];
    b = -x[1];
    d = x[2];
    return a > 0 && b > 0 && d > 0 &&
           y[0] == -a && y[1] == b && y[2] == -d &&
           z[0] == a && z[1] == -b && z[2] == -d;
}

struct PairMap {
    int M = 0;
    int P = 0;
    int gate_count = 0;
    vector<pair<int, int>> var_pair;
    vector<vector<int>> pair_var;
};

static PairMap infer_pairs(const CNF& cnf) {
    int gc = 0, a = 0, b = 0, d = 0;
    while (gate3(cnf.clauses, gc * 3, a, b, d)) ++gc;
    int M = 0;
    for (int x = 3; x <= 2000; ++x) {
        if (comb3(x) == gc) {
            M = x;
            break;
        }
    }
    if (!M) return {};
    int P = M * (M - 1) / 2;
    PairMap pm;
    pm.M = M;
    pm.P = P;
    pm.gate_count = gc;
    pm.var_pair.assign(P + 1, {-1, -1});
    pm.pair_var.assign(M, vector<int>(M, 0));
    auto set_pair = [&](int v, int i, int j) {
        if (v <= 0 || v > P || i == j) return;
        if (i > j) swap(i, j);
        pm.var_pair[v] = {i, j};
        pm.pair_var[i][j] = pm.pair_var[j][i] = v;
    };
    set_pair(1, 0, 1);
    vector<int> y0(M, 0);
    y0[1] = 1;
    for (int off = 0; off < M - 2; ++off) {
        gate3(cnf.clauses, off * 3, a, b, d);
        int j = off + 2;
        y0[j] = b;
        set_pair(b, 0, j);
        set_pair(d, 1, j);
    }
    vector<int> idx(P + 1, -1);
    for (int i = 1; i < M; ++i) idx[y0[i]] = i;
    for (int gi = 0; gi < gc; ++gi) {
        gate3(cnf.clauses, gi * 3, a, b, d);
        if (a <= P && b <= P && idx[a] >= 0 && idx[b] >= 0) {
            int i = idx[a], j = idx[b];
            if (i < j && !pm.pair_var[i][j]) set_pair(d, i, j);
        }
    }
    for (int v = 1; v <= P; ++v) {
        if (pm.var_pair[v].first < 0) return {};
    }
    return pm;
}

struct Group {
    enum Kind { STD, REM, Q, QCARRY } kind = STD;
    vector<int> out;
    vector<int> A;
    vector<int> B;
    int carry = 0;
};

static bool same_clause(const vector<int>& got, vector<int> exp) {
    if (got.size() != exp.size()) return false;
    vector<int> g = got;
    sort(g.begin(), g.end());
    sort(exp.begin(), exp.end());
    return g == exp;
}

struct GroupParser {
    const vector<vector<int>>& clauses;
    int P;
    int mod;

    vector<int> pos_auxs(const vector<int>& c) const {
        vector<int> r;
        for (int l : c) if (l > 0 && abs(l) > P) r.push_back(l);
        return r;
    }

    bool is_out_clause(const vector<int>& c, int out, int& lit) const {
        vector<int> pa = pos_auxs(c);
        if (pa.size() != 1 || pa[0] != out) return false;
        int other_count = 0, other = 0;
        for (int l : c) {
            if (l == out) continue;
            other = l;
            ++other_count;
        }
        if (other_count != 1) return false;
        lit = other;
        return true;
    }

    bool parse_standard(int i, Group& g, int& next) const {
        if (i >= (int)clauses.size()) return false;
        vector<int> pa = pos_auxs(clauses[i]);
        if (pa.size() != 1) return false;
        int out0 = pa[0], pos = i, lit = 0;
        vector<int> B, A;
        for (int j = 0; pos < (int)clauses.size(); ++j) {
            if (!is_out_clause(clauses[pos], out0 + j, lit)) break;
            B.push_back(lit);
            ++pos;
        }
        if (B.empty()) return false;
        for (int j = 0; pos < (int)clauses.size(); ++j) {
            if (!is_out_clause(clauses[pos], out0 + j, lit)) break;
            A.push_back(lit);
            ++pos;
        }
        if (A.empty()) return false;
        int L = (int)A.size() + (int)B.size();
        for (int ai = 1; ai <= (int)A.size(); ++ai) {
            for (int bj = 1; bj <= (int)B.size(); ++bj) {
                if (pos >= (int)clauses.size()) return false;
                if (!same_clause(clauses[pos], {A[ai - 1], B[bj - 1], out0 + ai + bj - 1})) return false;
                ++pos;
            }
        }
        g.kind = Group::STD;
        g.A = std::move(A);
        g.B = std::move(B);
        g.out.resize(L);
        iota(g.out.begin(), g.out.end(), out0);
        next = pos;
        return true;
    }

    bool parse_rem(int i, Group& g, int& next) const {
        if (i >= (int)clauses.size()) return false;
        const auto& c = clauses[i];
        vector<int> pa = pos_auxs(c);
        if (c.size() != 3 || pa.size() != 2) return false;
        vector<int> ordered;
        for (int l : c) if (l > 0 && abs(l) > P) ordered.push_back(l);
        int out0 = ordered[0], carry = ordered[1];
        int pos = i;
        vector<int> B, A;
        for (int j = 0; pos < (int)clauses.size(); ++j) {
            const auto& cl = clauses[pos];
            if (cl.size() == 3 && cl[0] < 0 && cl[1] == out0 + j && cl[2] == carry) {
                B.push_back(cl[0]);
                ++pos;
            } else break;
        }
        if (B.empty()) return false;
        for (int j = 0; pos < (int)clauses.size(); ++j) {
            const auto& cl = clauses[pos];
            if (cl.size() == 3 && cl[0] < 0 && cl[1] == out0 + j && cl[2] == carry) {
                A.push_back(cl[0]);
                ++pos;
            } else break;
        }
        if (A.empty()) return false;
        for (int ai = 1; ai <= (int)A.size(); ++ai) {
            for (int bj = 1; bj <= (int)B.size(); ++bj) {
                int s = ai + bj;
                vector<int> exp;
                if (s < mod) exp = {A[ai - 1], B[bj - 1], out0 + s - 1, carry};
                else if (s == mod) exp = {A[ai - 1], B[bj - 1], carry};
                else exp = {A[ai - 1], B[bj - 1], out0 + s - mod - 1};
                if (pos >= (int)clauses.size() || !same_clause(clauses[pos], exp)) return false;
                ++pos;
            }
        }
        g.kind = Group::REM;
        g.A = std::move(A);
        g.B = std::move(B);
        g.carry = carry;
        g.out.resize(mod - 1);
        iota(g.out.begin(), g.out.end(), out0);
        next = pos;
        return true;
    }

    bool parse_q_list(int& pos, int out0, int carry, vector<int>& items) const {
        int j = 1;
        while (pos < (int)clauses.size()) {
            const auto& c = clauses[pos];
            if (c.size() != 2 || c[0] >= 0 || c[1] != out0 + j - 1) break;
            int lit = c[0];
            items.push_back(lit);
            ++pos;
            if (pos >= (int)clauses.size()) return false;
            const auto& c2 = clauses[pos];
            if (same_clause(c2, {-carry, lit, out0 + j})) {
                ++pos;
            } else if (same_clause(c2, {-carry, lit})) {
                ++pos;
            } else {
                return false;
            }
            if (pos < (int)clauses.size() && clauses[pos].size() == 2 &&
                clauses[pos][0] < 0 && clauses[pos][1] == out0 + j) {
                ++j;
                continue;
            }
            break;
        }
        return true;
    }

    bool parse_q(int i, int carry, Group& g, int& next) const {
        if (i >= (int)clauses.size()) return false;
        const auto& c = clauses[i];
        if (c.size() != 2 || c[0] != -carry || c[1] <= P || c[1] <= 0) return false;
        int out0 = c[1], pos = i + 1;
        vector<int> B, A;
        int save_pos = pos;
        if (!parse_q_list(pos, out0, carry, B)) return false;
        if (B.empty()) {
            g.kind = Group::QCARRY;
            g.carry = carry;
            g.out = {out0};
            next = i + 1;
            return true;
        }
        if (!parse_q_list(pos, out0, carry, A) || A.empty()) return false;
        int maxidx = 1;
        for (int ai = 1; ai <= (int)A.size(); ++ai) {
            for (int bj = 1; bj <= (int)B.size(); ++bj) {
                if (pos >= (int)clauses.size()) return false;
                const auto& c1 = clauses[pos];
                if (c1.size() == 3 && c1[2] > 0 && same_clause({c1[0], c1[1]}, {A[ai - 1], B[bj - 1]})) {
                    maxidx = max(maxidx, c1[2] - out0 + 1);
                    ++pos;
                } else if (same_clause(c1, {A[ai - 1], B[bj - 1]})) {
                    ++pos;
                } else return false;
                if (pos >= (int)clauses.size()) return false;
                const auto& c2 = clauses[pos];
                if (c2.size() == 4 && c2[3] > 0 &&
                    same_clause({c2[0], c2[1], c2[2]}, {-carry, A[ai - 1], B[bj - 1]})) {
                    maxidx = max(maxidx, c2[3] - out0 + 1);
                    ++pos;
                } else if (same_clause(c2, {-carry, A[ai - 1], B[bj - 1]})) {
                    ++pos;
                } else return false;
            }
        }
        for (int k = i; k < pos; ++k) {
            for (int l : clauses[k]) {
                if (l > 0 && abs(l) > P && l != carry) maxidx = max(maxidx, l - out0 + 1);
            }
        }
        if (maxidx <= 0 || save_pos == pos) return false;
        g.kind = Group::Q;
        g.A = std::move(A);
        g.B = std::move(B);
        g.carry = carry;
        g.out.resize(maxidx);
        iota(g.out.begin(), g.out.end(), out0);
        next = pos;
        return true;
    }
};

static bool infer_mod(const CNF& cnf, const PairMap& pm, int& mod) {
    int start = pm.gate_count * 3;
    if (start >= (int)cnf.clauses.size()) return false;
    const auto& c = cnf.clauses[start];
    if (c.size() != 3 || c[0] >= 0 || c[1] <= pm.P || c[2] <= pm.P) return false;
    int out0 = c[1], carry = c[2], R = 0;
    while (start + R < (int)cnf.clauses.size()) {
        const auto& cl = cnf.clauses[start + R];
        if (cl.size() == 3 && cl[0] < 0 && cl[1] == out0 + R && cl[2] == carry) ++R;
        else break;
    }
    if (R <= 0 || R > 256) return false;
    mod = R + 1;
    return true;
}

static bool parse_groups(const CNF& cnf, const PairMap& pm, int mod, vector<Group>& groups) {
    GroupParser gp{cnf.clauses, pm.P, mod};
    int i = pm.gate_count * 3;
    int expect_carry = 0;
    while (i < (int)cnf.clauses.size()) {
        Group g;
        int next = i;
        if (expect_carry) {
            if (gp.parse_q(i, expect_carry, g, next)) {
                groups.push_back(std::move(g));
                i = next;
                expect_carry = 0;
                continue;
            }
            expect_carry = 0;
        }
        if (gp.parse_rem(i, g, next)) {
            expect_carry = g.carry;
            groups.push_back(std::move(g));
            i = next;
            continue;
        }
        if (gp.parse_standard(i, g, next)) {
            groups.push_back(std::move(g));
            i = next;
            continue;
        }
        ++i;
    }

    vector<unsigned char> covered(cnf.nvars + 1, 0);
    for (const Group& g : groups) {
        for (int v : g.out) {
            if (v <= pm.P || v > cnf.nvars || covered[v]) return false;
            covered[v] = 1;
        }
        if (g.kind == Group::REM) {
            int v = g.carry;
            if (v <= pm.P || v > cnf.nvars || covered[v]) return false;
            covered[v] = 1;
        }
    }
    for (int v = pm.P + 1; v <= cnf.nvars; ++v) {
        if (!covered[v]) return false;
    }
    return true;
}

static bool lit_true(const vector<signed char>& val, int lit) {
    int v = abs(lit);
    return val[v] && ((lit > 0 && val[v] > 0) || (lit < 0 && val[v] < 0));
}

static bool verify_model(const CNF& cnf, const vector<signed char>& val) {
    for (const auto& cl : cnf.clauses) {
        bool sat = false;
        for (int l : cl) {
            if (lit_true(val, l)) {
                sat = true;
                break;
            }
        }
        if (!sat) return false;
    }
    return true;
}

static bool build_full_model(const CNF& cnf, const PairMap& pm, const vector<Group>& groups,
                             const vector<int>& part, int mod, vector<signed char>& val) {
    val.assign(cnf.nvars + 1, 0);
    for (int v = 1; v <= pm.P; ++v) {
        auto [i, j] = pm.var_pair[v];
        val[v] = (part[i] == part[j]) ? 1 : -1;
    }

    vector<unsigned char> done(groups.size(), 0);
    int remaining = (int)groups.size();
    auto item_true = [&](int lit, bool& known) {
        int v = abs(lit);
        if (!val[v]) {
            known = false;
            return false;
        }
        known = true;
        return !lit_true(val, lit);
    };
    bool progress = true;
    while (remaining && progress) {
        progress = false;
        for (int gi = 0; gi < (int)groups.size(); ++gi) {
            if (done[gi]) continue;
            const Group& g = groups[gi];
            int cnt = 0;
            if (g.kind == Group::QCARRY) {
                if (!val[g.carry]) continue;
                cnt = val[g.carry] > 0 ? 1 : 0;
            } else {
                bool ok = true, known = true;
                int ca = 0, cb = 0;
                for (int l : g.A) {
                    bool t = item_true(l, known);
                    if (!known) { ok = false; break; }
                    if (t) ++ca;
                }
                if (!ok) continue;
                for (int l : g.B) {
                    bool t = item_true(l, known);
                    if (!known) { ok = false; break; }
                    if (t) ++cb;
                }
                if (g.kind == Group::REM) {
                    int sum = ca + cb;
                    cnt = sum % mod;
                    signed char cv = sum >= mod ? 1 : -1;
                    if (val[g.carry] && val[g.carry] != cv) return false;
                    val[g.carry] = cv;
                } else if (g.kind == Group::Q) {
                    if (!val[g.carry]) continue;
                    cnt = ca + cb + (val[g.carry] > 0 ? 1 : 0);
                } else {
                    cnt = ca + cb;
                }
            }
            for (int k = 0; k < (int)g.out.size(); ++k) {
                signed char vv = (cnt >= k + 1) ? 1 : -1;
                int v = g.out[k];
                if (val[v] && val[v] != vv) return false;
                val[v] = vv;
            }
            done[gi] = 1;
            --remaining;
            progress = true;
        }
    }
    if (remaining) return false;
    for (int v = 1; v <= cnf.nvars; ++v) if (!val[v]) return false;
    return verify_model(cnf, val);
}

static vector<vector<unsigned char>> extract_graph(const CNF& cnf, const PairMap& pm, vector<int>& sign) {
    sign.assign(pm.P + 1, 0);
    int start = pm.gate_count * 3;
    for (int ci = start; ci < (int)cnf.clauses.size(); ++ci) {
        for (int l : cnf.clauses[ci]) {
            int v = abs(l);
            if (v <= pm.P) {
                int s = l > 0 ? 1 : -1;
                if (sign[v] && sign[v] != s) return {};
                sign[v] = s;
            }
        }
    }
    vector<vector<unsigned char>> adj(pm.M, vector<unsigned char>(pm.M, 0));
    for (int v = 1; v <= pm.P; ++v) {
        if (!sign[v]) return {};
        auto [i, j] = pm.var_pair[v];
        adj[i][j] = adj[j][i] = sign[v] > 0;
    }
    return adj;
}

static int partition_cost(const vector<vector<unsigned char>>& adj, const vector<int>& part) {
    int M = (int)adj.size(), cost = 0;
    for (int i = 0; i < M; ++i) {
        for (int j = i + 1; j < M; ++j) {
            cost += ((part[i] == part[j]) != (bool)adj[i][j]);
        }
    }
    return cost;
}

static void normalize(vector<int>& part) {
    map<int, int> mp;
    int nxt = 0;
    for (int& x : part) {
        auto it = mp.find(x);
        if (it == mp.end()) it = mp.emplace(x, nxt++).first;
        x = it->second;
    }
}

struct SearchContext {
    const CNF& cnf;
    const PairMap& pm;
    const vector<Group>& groups;
    const vector<vector<unsigned char>>& adj;
    int mod;
    chrono::steady_clock::time_point deadline;
    vector<signed char> model;

    bool try_partition(const vector<int>& part) {
        vector<signed char> candidate;
        if (build_full_model(cnf, pm, groups, part, mod, candidate)) {
            model.swap(candidate);
            return true;
        }
        return false;
    }
};

static bool search_partition(SearchContext& ctx) {
    int M = ctx.pm.M;
    mt19937 rng(1);
    int best = INT_MAX;
    vector<int> best_part;
    int stale = 0;

    for (int restart = 0; chrono::steady_clock::now() < ctx.deadline && restart < 5000; ++restart) {
        vector<int> part(M);
        if (restart == 0) {
            iota(part.begin(), part.end(), 0);
        } else if (restart == 1) {
            fill(part.begin(), part.end(), 0);
        } else if (restart == 2) {
            vector<int> seen(M, 0);
            int comp = 0;
            for (int s = 0; s < M; ++s) {
                if (seen[s]) continue;
                vector<int> st = {s};
                seen[s] = 1;
                part[s] = comp;
                while (!st.empty()) {
                    int v = st.back();
                    st.pop_back();
                    for (int u = 0; u < M; ++u) {
                        if (!seen[u] && ctx.adj[v][u]) {
                            seen[u] = 1;
                            part[u] = comp;
                            st.push_back(u);
                        }
                    }
                }
                ++comp;
            }
            normalize(part);
        } else {
            int k = 2 + (int)(rng() % max(2, min(M, 16)));
            for (int i = 0; i < M; ++i) part[i] = (int)(rng() % k);
            normalize(part);
        }

        int cur = partition_cost(ctx.adj, part);
        bool improved = true;
        while (improved && chrono::steady_clock::now() < ctx.deadline) {
            improved = false;
            vector<int> order(M);
            iota(order.begin(), order.end(), 0);
            shuffle(order.begin(), order.end(), rng);
            for (int v : order) {
                int old = part[v];
                set<int> labs(part.begin(), part.end());
                int new_lab = labs.empty() ? 0 : (*labs.rbegin() + 1);
                int best_delta = 0, best_lab = old;
                for (int lab : labs) {
                    if (lab == old) continue;
                    int delta = 0;
                    for (int u = 0; u < M; ++u) {
                        if (u == v) continue;
                        delta += ((part[u] == lab) != (bool)ctx.adj[u][v]) -
                                 ((part[u] == old) != (bool)ctx.adj[u][v]);
                    }
                    if (delta < best_delta) best_delta = delta, best_lab = lab;
                }
                {
                    int lab = new_lab;
                    int delta = 0;
                    for (int u = 0; u < M; ++u) {
                        if (u == v) continue;
                        delta += ((part[u] == lab) != (bool)ctx.adj[u][v]) -
                                 ((part[u] == old) != (bool)ctx.adj[u][v]);
                    }
                    if (delta < best_delta) best_delta = delta, best_lab = lab;
                }
                if (best_delta < 0) {
                    part[v] = best_lab;
                    normalize(part);
                    cur += best_delta;
                    improved = true;
                }
            }

            vector<int> labs;
            {
                set<int> s(part.begin(), part.end());
                labs.assign(s.begin(), s.end());
            }
            bool merged = false;
            for (int ia = 0; ia < (int)labs.size() && !merged; ++ia) {
                for (int ib = ia + 1; ib < (int)labs.size(); ++ib) {
                    int a = labs[ia], b = labs[ib], delta = 0;
                    for (int i = 0; i < M; ++i) if (part[i] == a) {
                        for (int j = 0; j < M; ++j) if (part[j] == b) {
                            delta += (1 != (bool)ctx.adj[i][j]) - (0 != (bool)ctx.adj[i][j]);
                        }
                    }
                    if (delta < 0) {
                        for (int& x : part) if (x == b) x = a;
                        normalize(part);
                        cur += delta;
                        improved = merged = true;
                        break;
                    }
                }
            }
        }

        if (cur < best) {
            best = cur;
            best_part = part;
            stale = 0;
            if (ctx.try_partition(best_part)) return true;
        } else {
            ++stale;
        }
        if (restart > 100 && stale > 350) break;
    }
    return false;
}

static void print_model(const vector<signed char>& val) {
    printf("s SATISFIABLE\n");
    int n = (int)val.size() - 1;
    for (int i = 1; i <= n; ) {
        printf("v");
        for (int k = 0; k < 100 && i <= n; ++k, ++i) {
            printf(" %d", val[i] > 0 ? i : -i);
        }
        if (i > n) printf(" 0");
        printf("\n");
    }
}

int main(int argc, char** argv) {
    if (argc != 3) {
        fprintf(stderr, "usage: solver <formula.cnf> <out.pbp>\n");
        return 1;
    }
    CNF cnf = read_cnf(argv[1]);
    PairMap pm = infer_pairs(cnf);
    if (!pm.M || pm.P >= cnf.nvars) {
        printf("s UNKNOWN\n");
        return 0;
    }
    int mod = 0;
    if (!infer_mod(cnf, pm, mod)) {
        printf("s UNKNOWN\n");
        return 0;
    }
    vector<Group> groups;
    if (!parse_groups(cnf, pm, mod, groups)) {
        printf("s UNKNOWN\n");
        return 0;
    }
    vector<int> sign;
    auto adj = extract_graph(cnf, pm, sign);
    if (adj.empty()) {
        printf("s UNKNOWN\n");
        return 0;
    }

    SearchContext ctx{cnf, pm, groups, adj, mod,
                      chrono::steady_clock::now() + chrono::seconds(90), {}};
    if (search_partition(ctx)) {
        print_model(ctx.model);
        return 10;
    }
    printf("s UNKNOWN\n");
    return 0;
}
