#include <algorithm>
#include <chrono>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <numeric>
#include <limits>
#include <random>
#include <string>
#include <vector>

using Clock = std::chrono::steady_clock;

struct FastInts {
    FILE* f = nullptr;
    static constexpr size_t SZ = 1 << 20;
    char buf[SZ];
    size_t p = 0, n = 0;
    bool bol = true;

    explicit FastInts(const char* path) {
        f = std::fopen(path, "rb");
    }
    ~FastInts() {
        if (f) std::fclose(f);
    }
    int get() {
        if (p == n) {
            n = std::fread(buf, 1, SZ, f);
            p = 0;
            if (!n) return EOF;
        }
        return buf[p++];
    }
    bool next_int(int& out) {
        int c;
        while (true) {
            c = get();
            if (c == EOF) return false;
            if (bol && (c == 'c' || c == 'C')) {
                do {
                    c = get();
                } while (c != EOF && c != '\n');
                bol = true;
                if (c == EOF) return false;
                continue;
            }
            if (c == '\n') {
                bol = true;
                continue;
            }
            if (c == ' ' || c == '\t' || c == '\r') {
                continue;
            }
            bol = false;
            if (c == '-' || (c >= '0' && c <= '9')) break;
        }
        int sign = 1;
        if (c == '-') {
            sign = -1;
            c = get();
        }
        int v = 0;
        while (c >= '0' && c <= '9') {
            v = v * 10 + (c - '0');
            c = get();
        }
        if (c == '\n') bol = true;
        out = sign * v;
        return true;
    }
};

struct Graph {
    int nvar = 0, nclauses = 0, V = 0, K = 0;
    int words = 0;
    std::vector<uint64_t> adj;
    std::vector<int> degree;

    bool has_edge(int u, int v) const {
        return (adj[(size_t)u * words + (v >> 6)] >> (v & 63)) & 1ULL;
    }
    void add_edge(int u, int v) {
        if (u == v || has_edge(u, v)) return;
        adj[(size_t)u * words + (v >> 6)] |= 1ULL << (v & 63);
        adj[(size_t)v * words + (u >> 6)] |= 1ULL << (u & 63);
        degree[u]++;
        degree[v]++;
    }
};

static int popcount_words(const std::vector<uint64_t>& a, int off, int words) {
    int s = 0;
    for (int i = 0; i < words; ++i) s += __builtin_popcountll(a[off + i]);
    return s;
}

static bool parse_graph(const char* path, Graph& g) {
    FastInts in(path);
    if (!in.f) return false;
    int nvar, nclauses;
    if (!in.next_int(nvar) || !in.next_int(nclauses)) return false;
    g.nvar = nvar;
    g.nclauses = nclauses;

    std::vector<std::vector<int>> positive;
    std::vector<int> clause;
    clause.reserve(256);
    bool graph_ready = false;
    int K = 0, V = 0;
    long long binary_seen = 0;
    int x;
    while (in.next_int(x)) {
        clause.clear();
        while (x != 0) {
            clause.push_back(x);
            if (!in.next_int(x)) return false;
        }

        bool all_pos = true;
        for (int lit : clause) all_pos &= (lit > 0);
        if (!graph_ready && all_pos) {
            positive.push_back(clause);
            K = std::max(K, (int)clause.size());
            continue;
        }

        if (!graph_ready) {
            if (K <= 0 || nvar % K != 0) return false;
            V = nvar / K;
            if ((int)positive.size() != V) return false;
            for (int v = 0; v < V; ++v) {
                int want = std::min(v + 1, K);
                if ((int)positive[v].size() != want) return false;
                for (int c = 0; c < want; ++c) {
                    if (positive[v][c] != v * K + c + 1) return false;
                }
            }
            g.V = V;
            g.K = K;
            g.words = (V + 63) >> 6;
            g.adj.assign((size_t)V * g.words, 0);
            g.degree.assign(V, 0);
            graph_ready = true;
        }

        if (clause.size() != 2 || clause[0] >= 0 || clause[1] >= 0) return false;
        int a = -clause[0] - 1;
        int b = -clause[1] - 1;
        if (a < 0 || a >= nvar || b < 0 || b >= nvar) return false;
        int ca = a % K, cb = b % K;
        if (ca != cb) return false;
        int u = a / K, v = b / K;
        if (u < 0 || u >= V || v < 0 || v >= V || u == v) return false;
        g.add_edge(u, v);
        binary_seen++;
    }
    if (!graph_ready) return false;
    long long expected = 1LL * g.K;
    if (binary_seen % expected != 0) return false;
    return true;
}

struct Solver {
    const Graph& g;
    Clock::time_point deadline;
    std::mt19937_64 rng;
    std::vector<int> color;

    explicit Solver(const Graph& graph, double seconds)
        : g(graph), deadline(Clock::now() + std::chrono::milliseconds((int)(seconds * 1000))),
          rng(0x6a09e667f3bcc909ULL ^ (uint64_t)graph.V * 1000003ULL ^ graph.K) {}

    bool timed_out() const { return Clock::now() >= deadline; }
    int domain_size(int v) const { return std::min(v + 1, g.K); }

    bool greedy_once(bool randomize) {
        int V = g.V, K = g.K;
        int cwords = (K + 63) >> 6;
        std::vector<uint64_t> avail((size_t)V * cwords, 0);
        std::vector<int> avail_count(V), assigned(V, 0), col(V, -1);
        for (int v = 0; v < V; ++v) {
            int d = domain_size(v);
            for (int c = 0; c < d; ++c) avail[(size_t)v * cwords + (c >> 6)] |= 1ULL << (c & 63);
            avail_count[v] = d;
        }

        std::vector<std::pair<int,int>> changed;
        changed.reserve(V);
        for (int step = 0; step < V; ++step) {
            if ((step & 31) == 0 && timed_out()) return false;
            int best = -1, best_av = std::numeric_limits<int>::max(), best_deg = -1;
            for (int v = 0; v < V; ++v) if (!assigned[v]) {
                int av = avail_count[v];
                if (av < best_av || (av == best_av && g.degree[v] > best_deg)) {
                    best = v; best_av = av; best_deg = g.degree[v];
                }
            }
            if (best < 0 || best_av <= 0) return false;

            int chosen = -1, chosen_score = std::numeric_limits<int>::max();
            int d = domain_size(best);
            int start = randomize ? (int)(rng() % d) : 0;
            for (int ii = 0; ii < d; ++ii) {
                int c = (start + ii) % d;
                if (!((avail[(size_t)best * cwords + (c >> 6)] >> (c & 63)) & 1ULL)) continue;
                int score = 0;
                int widx = c >> 6;
                uint64_t bit = 1ULL << (c & 63);
                const uint64_t* row = &g.adj[(size_t)best * g.words];
                for (int word = 0; word < g.words; ++word) {
                    uint64_t bits = row[word];
                    while (bits) {
                        int nb = word * 64 + __builtin_ctzll(bits);
                        bits &= bits - 1;
                        if (nb < V && !assigned[nb] && (avail[(size_t)nb * cwords + widx] & bit)) score++;
                    }
                }
                if (score < chosen_score || (score == chosen_score && randomize && (rng() & 1))) {
                    chosen = c; chosen_score = score;
                }
            }
            if (chosen < 0) return false;
            assigned[best] = 1;
            col[best] = chosen;

            const uint64_t* row = &g.adj[(size_t)best * g.words];
            uint64_t bit = 1ULL << (chosen & 63);
            int widx = chosen >> 6;
            for (int word = 0; word < g.words; ++word) {
                uint64_t bits = row[word];
                while (bits) {
                    int nb = word * 64 + __builtin_ctzll(bits);
                    bits &= bits - 1;
                    if (nb < V && !assigned[nb] && (avail[(size_t)nb * cwords + widx] & bit)) {
                        avail[(size_t)nb * cwords + widx] &= ~bit;
                        avail_count[nb]--;
                    }
                }
            }
        }
        color.swap(col);
        return true;
    }

    bool local_search_once(int max_iters) {
        int V = g.V, K = g.K;
        std::vector<int> col(V, 0);
        std::vector<uint16_t> cnt((size_t)V * K, 0);

        std::vector<int> order(V);
        for (int i = 0; i < V; ++i) order[i] = i;
        std::sort(order.begin(), order.end(), [&](int a, int b) {
            return g.degree[a] > g.degree[b];
        });
        for (int v : order) {
            int d = domain_size(v);
            int bestc = 0, best = std::numeric_limits<int>::max();
            int start = (int)(rng() % d);
            for (int ii = 0; ii < d; ++ii) {
                int c = (start + ii) % d;
                int score = cnt[(size_t)v * K + c];
                if (score < best || (score == best && (rng() & 1))) {
                    best = score; bestc = c;
                }
            }
            col[v] = bestc;
            const uint64_t* row = &g.adj[(size_t)v * g.words];
            for (int word = 0; word < g.words; ++word) {
                uint64_t bits = row[word];
                while (bits) {
                    int nb = word * 64 + __builtin_ctzll(bits);
                    bits &= bits - 1;
                    if (nb < V) cnt[(size_t)nb * K + bestc]++;
                }
            }
        }

        std::vector<int> conflicts;
        conflicts.reserve(V);
        auto rebuild_conflicts = [&]() {
            conflicts.clear();
            long long edge_conf = 0;
            for (int v = 0; v < V; ++v) {
                if (cnt[(size_t)v * K + col[v]] > 0) conflicts.push_back(v);
                edge_conf += cnt[(size_t)v * K + col[v]];
            }
            return edge_conf / 2;
        };
        long long cur_conf = rebuild_conflicts();
        if (cur_conf == 0) { color.swap(col); return true; }

        for (int it = 0; it < max_iters; ++it) {
            if ((it & 4095) == 0) {
                if (timed_out()) return false;
                cur_conf = rebuild_conflicts();
                if (cur_conf == 0) { color.swap(col); return true; }
            }
            int v = conflicts[(size_t)(rng() % conflicts.size())];
            int oldc = col[v];
            int d = domain_size(v);
            int bestc = oldc, best = cnt[(size_t)v * K + oldc];
            int start = (int)(rng() % d);
            for (int ii = 0; ii < d; ++ii) {
                int c = (start + ii) % d;
                int score = cnt[(size_t)v * K + c];
                if (score < best || (score == best && c != oldc && (rng() & 3) == 0)) {
                    best = score; bestc = c;
                }
            }
            if (bestc == oldc) continue;
            col[v] = bestc;
            const uint64_t* row = &g.adj[(size_t)v * g.words];
            for (int word = 0; word < g.words; ++word) {
                uint64_t bits = row[word];
                while (bits) {
                    int nb = word * 64 + __builtin_ctzll(bits);
                    bits &= bits - 1;
                    if (nb < V) {
                        cnt[(size_t)nb * K + oldc]--;
                        cnt[(size_t)nb * K + bestc]++;
                    }
                }
            }
        }
        return false;
    }

    bool solve() {
        for (int i = 0; i < 16 && !timed_out(); ++i) {
            if (greedy_once(i != 0)) return true;
        }
        int restart = 0;
        while (!timed_out()) {
            int iters = 200000 + std::min(800000, restart * 50000);
            if (local_search_once(iters)) return true;
            restart++;
        }
        return false;
    }
};

static bool verify_cnf(const char* path, const Graph& g, const std::vector<int>& color) {
    std::vector<unsigned char> val(g.nvar + 1, 0);
    for (int v = 0; v < g.V; ++v) {
        int c = color[v];
        if (c < 0 || c >= g.K || c >= std::min(v + 1, g.K)) return false;
        val[v * g.K + c + 1] = 1;
    }

    FastInts in(path);
    if (!in.f) return false;
    int nvar, nclauses;
    if (!in.next_int(nvar) || !in.next_int(nclauses)) return false;
    if (nvar != g.nvar || nclauses != g.nclauses) return false;
    int x;
    long long seen = 0;
    while (in.next_int(x)) {
        bool sat = false;
        while (x != 0) {
            int var = x > 0 ? x : -x;
            if (var <= 0 || var > g.nvar) return false;
            bool lit = x > 0 ? val[var] : !val[var];
            sat |= lit;
            if (!in.next_int(x)) return false;
        }
        if (!sat) return false;
        seen++;
    }
    return seen == g.nclauses;
}

static std::vector<int> find_clique_larger_than_k(const Graph& g) {
    int V = g.V, W = g.words, target = g.K + 1;
    if (target > V) return {};
    std::vector<int> seeds(V);
    for (int i = 0; i < V; ++i) seeds[i] = i;
    std::sort(seeds.begin(), seeds.end(), [&](int a, int b) {
        return g.degree[a] > g.degree[b];
    });
    std::mt19937_64 rng(0x243f6a8885a308d3ULL ^ (uint64_t)V << 16 ^ g.K);
    int passes = std::min(24, std::max(4, V / 40));
    std::vector<uint64_t> cand(W), next(W);
    std::vector<int> clique;
    for (int pass = 0; pass < passes; ++pass) {
        if (pass) {
            std::shuffle(seeds.begin(), seeds.end(), rng);
            std::stable_sort(seeds.begin(), seeds.begin() + std::min(V, 96), [&](int a, int b) {
                return g.degree[a] > g.degree[b];
            });
        }
        int seed_limit = pass ? std::min(V, 160) : V;
        for (int si = 0; si < seed_limit; ++si) {
            int s = seeds[si];
            clique.clear();
            clique.push_back(s);
            for (int w = 0; w < W; ++w) cand[w] = g.adj[(size_t)s * W + w];
            while ((int)clique.size() < target) {
                int remaining = 0;
                for (int w = 0; w < W; ++w) remaining += __builtin_popcountll(cand[w]);
                if ((int)clique.size() + remaining < target) break;
                int best = -1, best_score = -1;
                for (int w = 0; w < W; ++w) {
                    uint64_t bits = cand[w];
                    while (bits) {
                        int v = w * 64 + __builtin_ctzll(bits);
                        bits &= bits - 1;
                        if (v >= V) continue;
                        int score = 0;
                        const uint64_t* row = &g.adj[(size_t)v * W];
                        for (int q = 0; q < W; ++q) score += __builtin_popcountll(row[q] & cand[q]);
                        if (score > best_score || (score == best_score && (rng() & 1))) {
                            best = v; best_score = score;
                        }
                    }
                }
                if (best < 0) break;
                clique.push_back(best);
                if ((int)clique.size() >= target) {
                    for (int i = 0; i < target; ++i) {
                        for (int j = i + 1; j < target; ++j) {
                            if (!g.has_edge(clique[i], clique[j])) return {};
                        }
                    }
                    clique.resize(target);
                    return clique;
                }
                const uint64_t* row = &g.adj[(size_t)best * W];
                for (int w = 0; w < W; ++w) cand[w] &= row[w];
            }
        }
    }
    return {};
}

static bool verify_clique_binary_clauses(const char* path, const Graph& g, const std::vector<int>& clique) {
    int q = (int)clique.size();
    if (q != g.K + 1) return false;
    for (int i = 0; i < q; ++i) {
        if (clique[i] < 0 || clique[i] >= g.V) return false;
        for (int j = i + 1; j < q; ++j) {
            if (clique[i] == clique[j] || !g.has_edge(clique[i], clique[j])) return false;
        }
    }

    std::vector<int> pos(g.V, -1);
    for (int i = 0; i < q; ++i) pos[clique[i]] = i;
    int cwords = (g.K + 63) >> 6;
    int pairs = q * (q - 1) / 2;
    std::vector<uint64_t> found((size_t)pairs * cwords, 0);

    FastInts in(path);
    if (!in.f) return false;
    int nvar, nclauses;
    if (!in.next_int(nvar) || !in.next_int(nclauses)) return false;
    if (nvar != g.nvar || nclauses != g.nclauses) return false;

    auto pair_index = [q](int a, int b) {
        if (a > b) std::swap(a, b);
        return a * q - (a * (a + 1)) / 2 + (b - a - 1);
    };

    std::vector<int> clause;
    clause.reserve(4);
    int x;
    long long seen = 0;
    while (in.next_int(x)) {
        clause.clear();
        while (x != 0) {
            clause.push_back(x);
            if (!in.next_int(x)) return false;
        }
        seen++;
        if (clause.size() != 2 || clause[0] >= 0 || clause[1] >= 0) continue;
        int a = -clause[0] - 1;
        int b = -clause[1] - 1;
        if (a < 0 || a >= g.nvar || b < 0 || b >= g.nvar) return false;
        int ca = a % g.K, cb = b % g.K;
        if (ca != cb) continue;
        int u = a / g.K, v = b / g.K;
        int iu = pos[u], iv = pos[v];
        if (iu < 0 || iv < 0 || iu == iv) continue;
        int pi = pair_index(iu, iv);
        found[(size_t)pi * cwords + (ca >> 6)] |= 1ULL << (ca & 63);
    }
    if (seen != g.nclauses) return false;
    uint64_t last_mask = (g.K & 63) ? ((1ULL << (g.K & 63)) - 1ULL) : ~0ULL;
    for (int p = 0; p < pairs; ++p) {
        for (int w = 0; w < cwords; ++w) {
            uint64_t want = (w == cwords - 1) ? last_mask : ~0ULL;
            if (found[(size_t)p * cwords + w] != want) return false;
        }
    }
    return true;
}

static bool write_clique_proof(const char* proof_path, const Graph& g, const std::vector<int>& clique) {
    if ((int)clique.size() != g.K + 1) return false;
    FILE* out = std::fopen(proof_path, "wb");
    if (!out) return false;

    auto var = [&](int vertex, int color) {
        return vertex * g.K + color + 1;
    };
    auto emit_id_sum = [&](const std::vector<long long>& ids) {
        if (ids.empty()) return;
        std::fprintf(out, "pol %lld", ids[0]);
        for (size_t i = 1; i < ids.size(); ++i) std::fprintf(out, " %lld +", ids[i]);
        std::fprintf(out, " ;\n");
    };

    std::fprintf(out, "pseudo-Boolean proof version 3.0\n");
    long long next_id = (long long)g.nclauses + 1;
    std::vector<long long> final_ids;
    final_ids.reserve((size_t)clique.size() + g.K);
    for (int v : clique) final_ids.push_back((long long)v + 1);

    for (int c = 0; c < g.K; ++c) {
        std::fprintf(out, "rup -1 x%d -1 x%d >= -1 ;\n", var(clique[0], c), var(clique[1], c));
        long long amo_id = next_id++;
        for (int j = 2; j < (int)clique.size(); ++j) {
            std::vector<long long> pair_ids;
            pair_ids.reserve(j);
            for (int i = 0; i < j; ++i) {
                std::fprintf(out, "rup -1 x%d -1 x%d >= -1 ;\n", var(clique[i], c), var(clique[j], c));
                pair_ids.push_back(next_id++);
            }
            std::fprintf(out, "pol %lld", amo_id);
            if (j - 1 != 1) std::fprintf(out, " %d *", j - 1);
            for (long long id : pair_ids) std::fprintf(out, " %lld +", id);
            std::fprintf(out, " %d d ;\n", j);
            long long new_amo_id = next_id++;
            std::fprintf(out, "del id %lld ;\n", amo_id);
            for (long long id : pair_ids) std::fprintf(out, "del id %lld ;\n", id);
            amo_id = new_amo_id;
        }
        final_ids.push_back(amo_id);
    }

    emit_id_sum(final_ids);
    std::fprintf(out, "output NONE ;\n");
    std::fprintf(out, "conclusion UNSAT : -1 ;\n");
    std::fprintf(out, "end pseudo-Boolean proof ;\n");
    bool ok = std::fclose(out) == 0;
    return ok;
}

static void print_model(const Graph& g, const std::vector<int>& color) {
    std::puts("s SATISFIABLE");
    const int per_line = 20;
    int cnt = 0;
    std::printf("v ");
    for (int v = 0; v < g.V; ++v) {
        for (int c = 0; c < g.K; ++c) {
            int var = v * g.K + c + 1;
            int lit = (c == color[v]) ? var : -var;
            std::printf("%d ", lit);
            if (++cnt == per_line) {
                std::printf("0\nv ");
                cnt = 0;
            }
        }
    }
    std::printf("0\n");
}

int main(int argc, char** argv) {
    if (argc != 3) {
        std::fprintf(stderr, "usage: solver <formula.cnf> <out.pbp>\n");
        return 1;
    }
    Graph g;
    if (!parse_graph(argv[1], g)) {
        std::puts("c unsupported CNF structure");
        std::puts("s UNKNOWN");
        return 0;
    }
    std::printf("c detected graph coloring: vertices=%d colors=%d edges=%lld\n",
                g.V, g.K, (long long)std::accumulate(g.degree.begin(), g.degree.end(), 0LL) / 2);
    std::vector<int> clique = find_clique_larger_than_k(g);
    if (!clique.empty()) {
        std::printf("c found clique certificate: size=%zu colors=%d\n", clique.size(), g.K);
        if (!verify_clique_binary_clauses(argv[1], g, clique)) {
            std::puts("c clique edge/color clauses failed proof precheck");
            std::puts("s UNKNOWN");
            return 0;
        }
        if (!write_clique_proof(argv[2], g, clique)) {
            std::puts("c failed to write VeriPB proof");
            std::puts("s UNKNOWN");
            return 0;
        }
        std::puts("s UNSATISFIABLE");
        return 20;
    }
    Solver solver(g, 585.0);
    if (!solver.solve()) {
        std::puts("c no coloring found within local search budget");
        std::puts("s UNKNOWN");
        return 0;
    }
    if (!verify_cnf(argv[1], g, solver.color)) {
        std::puts("c internal coloring failed full CNF verification");
        std::puts("s UNKNOWN");
        return 0;
    }
    print_model(g, solver.color);
    return 10;
}
